没有写省级主体部分。
# 序
百度迁徙数据有两大块,一块是某一地区(省或市)的迁入人口的来源地数据,另一块是某地区(省或市)的迁出人口的目的地数据。而无论是迁入来源地还是迁出目的地数据,它们都又分城市级别和省份级别两个层级。
# 主函数
刚学 Python,这也是写的第一个 crawler,各种意义上写得很烂。
我在主函数中让用户进行选择操作,并在主函数中进行批量请求、文件保存。导致界面非常不美观。
import urllib.request
import urllib.parse
import func
import time
area_choice = "area_choice"
while not area_choice.isnumeric():
area_choice = input("1.成都 2.海口")
scale_choice = "scale_choice"
while not scale_choice.isnumeric():
scale_choice = input("1.省级 2.市级")
direction_choice = "direction_choice"
while not direction_choice.isnumeric():
direction_choice = input("1.迁入来源地 2.迁出目的地")
date_choice = "date_choice"
while not date_choice.isnumeric():
date_choice = input("1.2022春运 2.2021国庆 3.2021春运 4.2020国庆 5.2020春运 6.自定义")
base_url = func.process_scale_choice(scale_choice=scale_choice)
date_se_list = func.process_date_choice(date_choice=date_choice)
date_list = func.generate_date_list(date_se_list)
dir_name = func.create_directory(area_choice, date_choice, direction_choice, scale_choice)
for date in date_list:
request = func.init_request(scale_choice, date, direction_choice, area_choice)
response = urllib.request.urlopen(request)
content = response.read().decode("utf-8")
content = content.split("(")[1].split(")")[0]
file = open("./%s/%s.json" % (dir_name, date), mode="w")
file.write(content)
file.close()
print("%s.json写入完毕" % date)
func.create_xlsx("./%s/%s.json" % (dir_name, date), date, scale_choice)
time.sleep(5)
print("结束")
# func
我给存放功能函数的文件取名为 func.py ,下面这部分记录详细的功能构成。
处理尺度选择
百度迁徙数据分市级尺度和省级尺度,不同尺度对应不同URL。 ? 之后写入其它参数。首先让用户自己选择尺度。
- 用户输入 1 以选择省级尺度。
- 用户输入 2 以选择市级尺度。
用户选择后,在此处进行处理,并返回对应的URL,以进行后续URL拼接。
def process_scale_choice(scale_choice):
base_url = ""
scale_choice = int(scale_choice)
if scale_choice == 1:
base_url = "https://huiyan.baidu.com/migration/provincerank.jsonp?"
else:
base_url = "https://huiyan.baidu.com/migration/cityrank.jsonp?"
return base_url
处理日期
迁徙数据包括二〇二〇年至二〇二二年春运、国庆多个时间段。除此之外,程序应该加入 自定义 时间段以使其更加人性化。以下代码涉及时间的格式化。 strftime() 是将时间格式化(format),并以字符串形式输出(str)。下面的 tiny_list 仅包括起始日期和终止日期两个时间点。
- 用户输入 1 以选择二〇二二春运时间段。
- 用户输入 2 以选择二〇二一国庆时间段。
- 用户输入 3 以选择二〇二一春运时间段。
- 用户输入 4 以选择二〇二〇国庆时间段。
- 用户输入 5 以选择二〇二〇春运时间段。
- 用户输入 6 以选择自定义时间段。
def process_date_choice(date_choice):
tiny_list = []
if int(date_choice) == 1:
tiny_list = ["20220110", time.strftime("%Y%m%d", time.localtime(time.time()))]
elif int(date_choice) == 2:
tiny_list = ["20210913", "20220109"]
elif int(date_choice) == 3:
tiny_list = ["20210119", "20210308"]
elif int(date_choice) == 4:
tiny_list = ["20200922", "20210118"]
elif int(date_choice) == 5:
tiny_list = ["20200110", "20200315"]
elif int(date_choice) == 6:
tiny_list.append(input("请输入起始日期,格式:20210101"))
tiny_list.append(input("请输入终止日期,格式:20210101"))
return tiny_list
判断用户选择的时间段后,进行日期列表生成(字符串的列表)。之后在URL拼接中,每一个日期都将进行一次拼接,并请求。日期生成时应该按照 yyyymmdd 的格式。
def generate_date_list(date_se_list:list):
pandas_date_list = pandas.date_range(date_se_list[0], date_se_list[1], freq="1D")
date_list = []
for date in pandas_date_list:
date_list.append(date.strftime("%Y%m%d"))
return date_list
方向选择
百度迁徙数据分迁入数据和迁出数据,不同迁徙方向对应不同参数。
def process_direction_choice(direction_choice):
direction_choice = int(direction_choice)
direction_str = ""
if direction_choice == 1:
direction_str = "move_in"
elif direction_choice == 2:
direction_str = "move_out"
return direction_str
处理地区选择
百度迁徙地图提供全国各省市的迁徙数据,因此需要指定地区以获取相应的数据。此处只贴出两个地区的 area_code ,想要获取更多地区编码,可以在百度迁徙地图中,选择目标地区并获取其返回的json,json里有对应的地区编码。
def process_area_choice(area_choice):
area_choice = int(area_choice)
area_code = ""
if area_choice == 1:
area_code = "510100"
elif area_choice == 2:
area_code = "460100"
return area_code
创建对应的文件夹以存储JSON和XLSX
创建相应名称的文件夹以更好地管理获取到的文件。该 方法 需要传入用户的地区选择、日期选择、方向选择与尺度选择。并写入非常多的 if 语句进行判断,然后得到正确的文件夹名称。
得到文件夹名称之后,判断是否已存在该文件夹。如果是,则提示用户已存在,并扬言要覆盖里面的内容。事实上程序会这么执行。如果否,则使用 os.mkdir() 方法创建文件夹。
def create_directory(area_choice, date_choice, direction_choice, scale_choice):
area_choice = int(area_choice)
date_choice = int(date_choice)
direction_choice = int(direction_choice)
scale_choice = int(scale_choice)
if area_choice == 1:
area = "成都"
elif area_choice == 2:
area = "海口"
if date_choice == 1:
date_range = "2022春运"
elif date_choice == 2:
date_range = "2021国庆"
elif date_choice == 3:
date_range = "2021春运"
elif date_choice == 4:
date_range = "2020国庆"
elif date_choice == 5:
date_range = "2020春运"
elif date_choice == 6:
date_range = "自定义"
if direction_choice == 1:
direction = "迁入来源地"
elif direction_choice == 2:
direction = "迁出目的地"
if scale_choice == 1:
scale = "省级"
elif scale_choice == 2:
scale = "市级"
dir_name = "%s_%s_%s_%s" % (area, date_range, scale, direction)
dir_exists = os.path.exists(dir_name)
if dir_exists:
print("\"" + dir_name + "\"" + "目录已存在,将覆盖目录里同名文件")
else:
os.mkdir(dir_name)
return dir_name
批量请求并存储得到的JSON文件
使用 func 里的方法拼接URL,并在 main 函数里进行请求。没办法,我才学 Python,这也是我写的第一个 crawler,写得确实丑。
def init_request(scale_choice, date, direction_choice, area_choice):
base_url = process_scale_choice(scale_choice=scale_choice)
direction = process_direction_choice(direction_choice)
area_code = process_area_choice(area_choice)
para = {
# 如果主体为省,则需要改dt
"dt": "city",
"id": area_code,
"type": direction,
"date": date
}
url = base_url + urllib.parse.urlencode(para)
my_headers = {"User-Agent": "Mozilla/5.0"}
request = urllib.request.Request(url=url, headers=my_headers)
return request
与此同时在 main 函数,将 date_list 里的时间依次取出并进行URL拼接和请求。将请求得到的数据使用 split() 方法进行裁剪,得到符合JSON格式标准的数据,并存入到本地JSON文件。
for date in date_list:
request = func.init_request(scale_choice, date, direction_choice, area_choice)
response = urllib.request.urlopen(request)
content = response.read().decode("utf-8")
content = content.split("(")[1].split(")")[0]
file = open("./%s/%s.json" % (dir_name, date), mode="w")
file.write(content)
file.close()
print("%s.json写入完毕" % date)
func.create_xlsx("./%s/%s.json" % (dir_name, date), date, scale_choice)
time.sleep(5)
打开JSON文件获取其数据并创建XLSX
在市级数据中,我想把所在省的信息也加上去,所以对 scale_choice 进行了判断。
在此处第一次接触 pandas 库,进行 Excel 创建。注意事项详见最后一章。
def create_xlsx(json_path, date, scale_choice):
json_object = json.load(open(json_path, encoding="utf-8"))
scale_choice = int(scale_choice)
if scale_choice == 1:
province_list = jsonpath.jsonpath(json_object, "$.data.list[*].province_name")
value_list = jsonpath.jsonpath(json_object, "$.data.list[*].value")
if province_list and value_list:
df = pandas.DataFrame({
"Province_Name": province_list,
"Proportion": value_list
})
df.to_excel("./%s/%s.xlsx" % (json_path.split("/")[1], date))
print("%s.xlsx写入完成" % date)
else:
print("%s.xlsx写入失败,未读取到json数据" % date)
elif scale_choice == 2:
province_list = jsonpath.jsonpath(json_object, "$.data.list[*].province_name")
city_list = jsonpath.jsonpath(json_object, "$.data.list[*].city_name")
value_list = jsonpath.jsonpath(json_object, "$.data.list[*].value")
if province_list and city_list and value_list:
df = pandas.DataFrame({
"Province_Name": province_list,
"City_Name": city_list,
"Proportion": value_list
})
df.to_excel("./%s/%s.xlsx" % (json_path.split("/")[1], date))
print("%s.xlsx写入完成" % date)
else:
print("%s.xlsx写入失败,json数据内没有内容或未获取json数据" % date)
# 注意事项
在实现批量获取百度迁徙数据时,遇到并解决了很多新需求和新问题,以下部分则记录所学。
新建XLSX并保存:
在保存excel文件时程序报错,因为 pandas.DataFrame 的 to_excel() 函数需要 openpyxl 库。
打开 cmd:
pip install openpyxl
写入XLSX:
第一次使用 pandas 来写入excel文件,记录以下如何实现的。
import pandas
# 写入DataFrame
df = pandas.DataFrame({“列1”: list1, "列2": list2...})
# 创建excel
df.to_excel("new_excel.xlsx", index=False)
生成一系列时间:
使用 pandas 的 date_range() 创建的时间需要格式化成需要的样式。
import pandas
# 起始日期为2022年2月2日,终止为2022年2月7日,间隔为1天
pandas_date_list = pandas.date_range("20220202", "20220207", freq="1D")
# 新建列表来装格式化后的pandas_date
date_list = []
# 使用for循环来格式化pandas_date
for date in pandas_date_list:
#"%Y%m%d"就是20220202这种日期格式
date_list.append(date.strftime("%Y%m%d"))
创建文件夹:
import os
dir_exists = os.path.exists(dir_name)
if dir_exists:
print("\"" + dir_name + "\"" + "目录已存在,将覆盖目录里同名文件")
else:
os.mkdir(dir_name)