百度迁徙数据_Crawling

没有写省级主体部分。


#

百度迁徙数据有两大块,一块是某一地区(省或市)的迁入人口的来源地数据,另一块是某地区(省或市)的迁出人口的目的地数据。而无论是迁入来源地还是迁出目的地数据,它们都又分城市级别和省份级别两个层级。

# 主函数

刚学 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 ,想要获取更多地区编码,可以在百度迁徙地图中,选择目标地区并获取其返回的jsonjson里有对应的地区编码。

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)
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments