存档2020年6月15日

Python获取中国日历

在做项目排班的时候经常会遇到一个问题,就是如何在Excel中标记中国的节假日?
经过在网上找各种API,发现不是要钱就是服务器404,再不然就是没有标记完全(比如只标记了节假日,但调休加班没有)。所以,最后还是背靠度娘,通过度娘提供的数据来自己做一个带中国节假日日历列表。

度娘的接口

https://sp0.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query=2020%E5%B9%B4&resource_id=6018&format=json

可以看到query对应的值就是你要查询的年月对应的日历。这里有个好处就是,返回的数据会包含当年的所有节假日和调休加班日,如下:

image.png

所以,可以在holiday的list中看到当年所有的放假和调休加班(放假对应的status是1,而调休加班则是2),再结合日历本身的周六周日,就可以对当年所有的日期进行标记,最终输出某一天是法定放假还是法定上班了。

以下是,所有代码

以下是对小白的操作

  • 用法就是,复制这些文本
  • 在本地存储为任意一个text文档
  • 将此文档的后缀名由”.txt”修改为”.py”
  • 然后使用python运行此文件
  • 要修改年份,只需要调整最后的year=2020这个代码就可以了

# -*- coding: utf-8 -*-
"""
Created on Mon Jun  8 17:18:15 2020

@author: 西湖味精三月鲜
"""

# 百度获取日历
# https://sp0.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query=2020%E5%B9%B4&resource_id=6018&format=json


import pandas as pd
import requests
import json
import numpy as np

# 生成某一年的日历
def gen_calendar(year=2020):
    # 生成日历列表
    start_date=str(year)+'0101'
    end_date=str(year)+'1231'
    df=pd.DataFrame()
    dt=pd.date_range(start_date, end_date,freq='1D')
    df['date']=dt
    # 计算周几
    df['dayofweek']=df['date'].dt.dayofweek+1
    # 获取法定节假日
    up1='https://sp0.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query='
    up2='%E5%B9%B4&resource_id=6018&format=json'
    url="".join([up1,str(year),up2])
    r=requests.get(url)
    r_json=json.loads(r.text)
    # 获取放假信息
    h_data=r_json['data'][0]['holiday']
    h_df=pd.DataFrame()
    for i in range(len(h_data)):
        each_d=h_data[i]['list']
        each_df=pd.DataFrame(each_d)
        h_df=h_df.append(each_df)
    # 处理一下数据,去重等等
    h_df.drop_duplicates(inplace=True)
    h_df.reset_index(drop=True,inplace=True)
    h_df['date']=pd.to_datetime(h_df['date'])
    
    # 合并数据
    df2=pd.merge(df,h_df,how='left',on='date')
    df2.fillna(0,inplace=True)
    df2['status']=df2['status'].astype('int')
    # 返回是否假日
    judge=np.where(df2['dayofweek']<6,0,1)+df2['status']
    judge=np.where((judge==2) | (judge==1),'Y','N')
    df2['isholiday']=judge
    
    return df2



in __name__=="__main__":
    year=2020
    df=gen_calendar(year)
    df2.to_csv('{}年节日历表.csv'.format(year),index=False)

以下是懒人入口,2011-2020年的都在这里

  • 只有2011年及之后年份的数据
  • 只有国务院公布了放假安排后,才会有当年的数据,比如现在就没有2021年的
  • dayofweek是星期几
  • status是放假安排的判断,1表示法定放假,2表示法定上班,0没有特殊意义
  • isholiday就是最后的结果了,Y表示是法定节假日,N表示是要上班的日子
  • 下载链接:
    链接:https://pan.baidu.com/s/1M1ehYSAVoLt-WvHlf4EM4Q 提取码:s7wm

Python网站手机号码解密的一种方法

黄页88网站反爬措施对手机号进行了一些加密处理。这里介绍一下怎么解密手机号码的方法。

首先,我们看一下网站显示的手机号是这样的:

显示的手机号

使用ctrl c复制粘贴后变成了这样:

复制后的手机号码

而实际上,用Python爬虫获取到的内容是这样的:

爬虫获取到的数据

经过分析,发现这些数据其实有以下特点:

  1. 每次刷新网页,编码会变
  2. 编码之间其实有关系(实际上就是0-9转换成了16进制)

所以,我们的思路就是:

  1. 确定手机号码开头1对应的是第一个编码
  2. 然后根据第一个编码获取0-9对应的16进制编码
  3. 在通过编码检索出这串编码对应的电话号码

步骤:

  1. 首先爬取到电话号码的代码

code_list='&#x882b7;&#x882bb;&#x882bc;&#x882b9;&#x882b7;&#x882b8;&#x882bc;&#x882bd;&#x882bd;&#x882bd;&#x882b7'

  1. 要转换为16进制,需要把&#替换为0

# 替换&#为0,用于后面直接转换为10进制数
code_list=code_list.replace("&#","0")

In [4]: code_list
Out[4]: '0x882b7;0x882bb;0x882bc;0x882b9;0x882b7;0x882b8;0x882bc;0x882bd;0x882bd;0x882bd;0x882b7'
  1. 上面是个字符串,要对每个编码进行处理,还是需要转换成列表,刚好对应11个号码

# 转换成列表
code_list=code_list.split(';')

In [6]: code_list
Out[6]: 
['0x882b7',
 '0x882bb',
 '0x882bc',
 '0x882b9',
 '0x882b7',
 '0x882b8',
 '0x882bc',
 '0x882bd',
 '0x882bd',
 '0x882bd',
 '0x882b7']
  1. 因为手机号码的第一位是1,所以确定第一位16进制对应的10进制值,就可以知道0-9对应的10进制值是多少

# 确定第一个号码1对应的10进制值
c1=int(code_list[0],base=16)

In [8]: c1
Out[8]: 557751
  1. 知道了1对应的值,就创建0-9对应的10进制值

# 创建0-9对应的10进制值
int_list=range(c1-1,c1+9)

In [10]: int_list
Out[10]: range(557750, 557760)
  1. 然后将上面的数据转换成16进制

# 将其转换为hex
hex_list=[str(hex(i)) for i in int_list]

In [12]: hex_list
Out[12]: 
['0x882b6',
 '0x882b7',
 '0x882b8',
 '0x882b9',
 '0x882ba',
 '0x882bb',
 '0x882bc',
 '0x882bd',
 '0x882be',
 '0x882bf']
  1. 创建0-9的列表,并转换成字符串,方便后面组装成电话号码(这里0-9的顺序和hex_list里面的顺序是一一对应的)

# 创建0-9的数字对应列表
str_list=[str(i) for i in range(0,10)]

In [14]: str_list
Out[14]: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
  1. 将hex和0-9的字符串组装成字典,方便后面查询

# 组装成字典方便对应
code_dict=dict(zip(hex_list,str_list))

In [16]: code_dict
Out[16]: 
{'0x882b6': '0',
 '0x882b7': '1',
 '0x882b8': '2',
 '0x882b9': '3',
 '0x882ba': '4',
 '0x882bb': '5',
 '0x882bc': '6',
 '0x882bd': '7',
 '0x882be': '8',
 '0x882bf': '9'}
  1. 最后一步就是通过字典查询并组装成最终的电话号码了(为了避免各种纠纷:这里的电话号码后面几位进行了修改,不是真实的用户电话)

# 见证奇迹的时刻,把电话号码翻译过来
phone="".join([code_dict[p] for p in code_list])

In [18]: phone
Out[18]: '15631267771'

所有代码如下:

code_list='&#x882b7;&#x882bb;&#x882bc;&#x882b9;&#x882b7;&#x882b8;&#x882bc;&#x882bd;&#x882bd;&#x882bd;&#x882b7'
# 替换&#为0,用于后面直接转换为10进制数
code_list=code_list.replace("&#","0")
# 转换成列表
code_list=code_list.split(';')
# 确定第一个号码1对应的10进制值
c1=int(code_list[0],base=16)

# 创建0-9对应的10进制值
int_list=range(c1-1,c1+9)

# 将其转换为hex
hex_list=[str(hex(i)) for i in int_list]

# 创建0-9的数字对应列表
str_list=[str(i) for i in range(0,10)]

# 组装成字典方便对应
code_dict=dict(zip(hex_list,str_list))

# 见证奇迹的时刻,把电话号码翻译过来
phone="".join([code_dict[p] for p in code_list])

上面这个过程最主要的一点是,我们假设编码的第一位是1,其他号码都是通过这个推算来的。但无法避免有些手机号码填错(第一位不是1),或者填的是固定电话的情况,所以无法适用于这些情况。