政府数据公开接口如何用python调用?

问题遇到的现象和发生背景:
在山东公共数据开放网
以下是HTTP协议的传输方式,实际的代码调用服务必须遵循以下格式。
X-Client-Id:令牌标识。 点此获取令牌
X-Timestamp:调用时间戳,值为当前时间的毫秒数,也就是从1970年1月1日起至今的时间转换为毫秒,时间戳有效时间为10分钟。
X-Nonce:调用方生成的随机数。
X-Signature:调用方生成的签名值,生成方式是X-Client-Id+X-Timestamp+X-Nonce组合字符,使用HmacSHA256算法计算并经Base64编码后的字符串,密钥为签名认证令牌密钥。

我想要达到的结果:
目前根据ChatGPT已经完成了代码,但是在"response = requests.get(api_endpoint, headers=headers, data=data, timeout=(5,10))"卡住了,获取不了信息。原始代码如下

import requests
import csv
import time
import hmac
import hashlib
import base64
import os
import random
import urllib.parse

client_id = " "
client_secret = " "  # 你的客户端密钥
api_endpoint = "http://data.sd.gov.cn/gateway/api/1/gpsfy"
output_directory = " "  # 指定CSV输出目录

# 定义每次请求获取的数据数量和分批导出的数据数量
page_size = 50
export_batch_size = 5000000  # 每500万条数据导出一个CSV文件

# 计算总共需要多少次请求
total_records = 559249349  # 总记录数
total_pages = (total_records + page_size - 1) // page_size  # 总页数

# 准备CSV文件的列名
csv_columns = ["vehicle_no", "altitude", "add_time", "speed", "sim", "datetime", "lon", "state", "deviceno", 
               "direction", "mileage", "lat"]

# 初始化时间戳
timestamp = int(time.time() * 1000)  # 当前时间的毫秒数

# 初始化CSV文件
batch_number = 0
csv_filename = f"data_batch_{batch_number}.csv"
csv_filepath = os.path.join(output_directory, csv_filename)

# 打开第一个CSV文件
with open(csv_filepath, 'w', newline='') as csv_file:
    csv_writer = csv.DictWriter(csv_file, fieldnames=csv_columns)
    csv_writer.writeheader()

    # 发送多次HTTP请求,每次获取一页数据
    for page in range(2, total_pages + 1):
        # 验证时间戳是否失效,如果失效则更新时间戳
        current_timestamp = int(time.time() * 1000)
        if current_timestamp - timestamp > 600000:  # 时间戳有效时间为10分钟(600,000毫秒)
            timestamp = current_timestamp

        # 创建签名
        nonce = str(random.randint(1, 999999))  # 随机数
        text_to_sign = client_id + str(timestamp) + nonce
        # 使用client_secret创建HMAC-SHA256哈希
        key = bytes(client_secret, 'utf-8')
        message = bytes(text_to_sign, 'utf-8')
        hash = hmac.new(key, message, hashlib.sha256)
        
        # 将哈希值转换为Base64编码的字符串
        signature = base64.b64encode(hash.digest()).decode('utf-8')
        if page<10:
            print(signature)

        # 创建HTTP头部
        headers = {
            "X-Client-Id": client_id,
            "X-Timestamp": str(timestamp),
            "X-Nonce": nonce,
            "X-Signature": signature
        }
        
        if page<10:
            print(headers)

        # 构建请求参数(需要对参数进行URL编码)
        data = {
            "PAGE": page,
            "PAGENUM": page_size
        }

        # 发送HTTP请求
        response = requests.get(api_endpoint, headers=headers, data=data, timeout=(5,10))
        if page<10:
            print("Request URL:", response.url)
            print("Request Headers:", response.request.headers)
            print("Request Parameters:", response.request.params)
            
        # 处理响应数据
        if response.status_code == 200:
            data = response.json()  # 假设API返回JSON数据

            # 提取参数并写入CSV文件
            for entry in data:
                row_data = {
                    "vehicle_no": entry["vehicle_no"],
                    "altitude": entry["altitude"],
                    "add_time": entry["add_time"],
                    "speed": entry["speed"],
                    "sim": entry["sim"],
                    "datetime": entry["datetime"],
                    "lon": entry["lon"],
                    "state": entry["state"],
                    "deviceno": entry["deviceno"],
                    "direction": entry["direction"],
                    "mileage": entry["mileage"],
                    "lat": entry["lat"]
                }
                csv_writer.writerow(row_data)

            # 如果达到了分批导出的数据数量,创建新的CSV文件
            if total_records >= export_batch_size and total_records % export_batch_size == 0:
                batch_number += 1
                csv_filename = f"data_batch_{batch_number}.csv"
                csv_filepath = os.path.join(output_directory, csv_filename)

                # 打开新的CSV文件
                with open(csv_filepath, 'w', newline='') as new_csv_file:
                    csv_writer = csv.DictWriter(new_csv_file, fieldnames=csv_columns)
                    csv_writer.writeheader()
        else:
            print(f"请求失败,HTTP状态码: {response.status_code}")

print("数据导出完成")

你这代码是自己写的吗?接口还没有调用成功了,都写了一大堆其他的代码?你先把接口调用的逻辑调通,然后再网上加其他的功能,或者你不会的话联系我,我帮你调一下,或者你把生成的请求内容打印出来,去postman里面先请求一下。代码我就不具体看了,没有账号密钥也没法请求

根据代码逻辑,以下是一些可能需要修改的地方:

  1. 确保 client_idclient_secret 参数正确,确保能够通过认证获取数据。

  2. 确保 output_directory 参数指定的 CSV 输出目录存在。

  3. 由于响应数据是 JSON 格式的,需要使用 response.json() 方法将其转换为 Python 对象,以便提取数据。

  4. 可能需要手动对某些参数进行 URL 编码,例如 data 参数。可以使用 urllib.parse.urlencode() 方法对参数进行编码,例如:urllib.parse.urlencode(data)

  5. 如果获取到的数据量很大,可能需要将每个 CSV 文件分成多个批次导出,以避免一次性处理过多数据。可以根据需要修改 export_batch_size 参数,控制每个 CSV 文件包含的数据数量。

  6. 如果出现请求失败的情况,可能需要检查是否超时,可以根据需要调整 timeout 参数。还可以考虑添加重试机制,即在请求失败的情况下,重复尝试发送相同的请求,直到获取到数据为止。

引用 皆我百晓生 小程序回复内容作答:
在您提供的代码中,问题可能出在以下几个地方:

  1. 请求的URL是否正确。请确认api_endpoint的值是否正确,确保可以通过浏览器等方式访问到数据接口。

  2. 请求的headers参数是否正确。请检查X-Client-IdX-TimestampX-NonceX-Signature的值是否正确。确保您已经正确生成签名值并将其赋值给X-Signature参数。

  3. 请求的data参数是否正确。请检查"PAGE""PAGENUM"的值是否正确。根据您的需求,您可能需要将这些参数进行适当的编码,例如使用urllib.parse.urlencode()方法。

同时,如果您未收到期望的响应,请检查网络连接是否正常,并确保您可以通过其他方式成功访问数据接口。

以下是对原始代码的一些修改建议:

  1. 移动时间戳的更新判断代码到请求之前,以确保每次请求都使用正确的时间戳。

  2. 使用requests.get()方法发送GET请求时,不要使用data参数,而应使用params参数传递查询参数。

  3. 在处理响应数据之前,建议检查响应的状态码。您可以使用response.status_code获取状态码,例如response.status_code == 200表示请求成功。

根据以上修改建议,您可以尝试修改您的代码如下:

import requests
import csv
import time
import hmac
import hashlib
import base64
import os
import random
import urllib.parse

client_id = " "
client_secret = " "  # 你的客户端密钥
api_endpoint = "http://data.sd.gov.cn/gateway/api/1/gpsfy"
output_directory = " "  # 指定CSV输出目录

# 定义每次请求获取的数据数量和分批导出的数据数量
page_size = 50
export_batch_size = 5000000  # 每500万条数据导出一个CSV文件

# 计算总共需要多少次请求
total_records = 559249349  # 总记录数
total_pages = (total_records + page_size - 1) // page_size  # 总页数

# 准备CSV文件的列名
csv_columns = ["vehicle_no", "altitude", "add_time", "speed", "sim", "datetime", "lon", "state", "deviceno",
               "direction", "mileage", "lat"]

# 初始化时间戳
timestamp = int(time.time() * 1000)  # 当前时间的毫秒数

# 初始化CSV文件
batch_number = 0
csv_filename = f"data_batch_{batch_number}.csv"
csv_filepath = os.path.join(output_directory, csv_filename)

# 打开第一个CSV文件
with open(csv_filepath, 'w', newline='') as csv_file:
    csv_writer = csv.DictWriter(csv_file, fieldnames=csv_columns)
    csv_writer.writeheader()

    # 发送多次HTTP请求,每次获取一页数据
    for page in range(2, total_pages + 1):
        # 验证时间戳是否失效,如果失效则更新时间戳
        current_timestamp = int(time.time() * 1000)
        if current_timestamp - timestamp > 600000:  # 时间戳有效时间为10分钟(600,000毫秒)
            timestamp = current_timestamp

        # 创建签名
        nonce = str(random.randint(1, 999999))  # 随机数
        text_to_sign = client_id + str(timestamp) + nonce
        # 使用client_secret创建HMAC-SHA256哈希
        key = bytes(client_secret, 'utf-8')
        message = bytes(text_to_sign, 'utf-8')
        hash = hmac.new(key, message, hashlib.sha256)

        # 将哈希值转换为Base64编码的字符串
        signature = base64.b64encode(hash.digest()).decode('utf-8')
        if page < 10:
            print(signature)

        # 创建HTTP头部
        headers = {
            "X-Client-Id": client_id,
            "X-Timestamp": str(timestamp),
            "X-Nonce": nonce,
            "X-Signature": signature
        }

        if page < 10:
            print(headers)

        # 构建请求参数(需要对参数进行URL编码)
        params = {
            "PAGE": page,
            "PAGENUM": page_size
        }

        # 发送HTTP请求
        response = requests.get(api_endpoint, headers=headers, params=params, timeout=(5, 10))
        if page < 10:
            print("Request URL:", response.url)
            print("Request Headers:", response.request.headers)
            print("Request Parameters:", response.request.params)

        # 处理响应数据
        if response.status_code == 200:
            data = response.json()  # 假设API返回JSON数据

            # 提取参数并写入CSV文件
            for entry in data:
                row_data = {
                    "vehicle_no": entry["vehicle_no"],
                    "altitude": entry["altitude"],
                    "add_time": entry["add_time"],
                    "speed": entry["speed"],
                    "sim": entry["sim"],
                    "datetime": entry["datetime"],
                    "lon": entry["lon"],
                    "state": entry["state"],
                    "deviceno": entry["deviceno"],
                    "direction": entry["direction"],
                    "mileage": entry["mileage"],
                    "lat": entry["lat"]
                }
                csv_writer.writerow(row_data)

            # 如果达到了分批导出的数据数量,创建新的CSV文件
            if total_records >= export_batch_size and total_records % export_batch_size == 0:
                batch_number += 1
                csv_filename = f"data_batch_{batch_number}.csv"
                csv_filepath = os.path.join(output_directory, csv_filename)

                # 打开新的CSV文件
                with open(csv_filepath, 'w', newline='') as new_csv_file:
                    csv_writer = csv.DictWriter(new_csv_file, fieldnames=csv_columns)
                    csv_writer.writeheader()
        else:
            print(f"请求失败,HTTP状态码: {response.status_code}")

print("数据导出完成")

请根据您的具体情况进行适当的修改,如填充client_idclient_secretoutput_directorytotal_records等变量,并根据需要修改和调试输出内容。希望以上信息对您有帮助!

需要把错误信息输出出来,查看报错的信息才可以判断

这个代码运行后没有报错信息,就是卡在response = requests.get(api_endpoint, headers=headers, params=params)这一步了

您的代码中将请求数据假设为 JSON 格式,并尝试使用 response.json() 来解析响应数据。确保 API 返回的数据确实是 JSON 格式。如果不是 JSON 格式,您需要根据实际情况来解析响应数据。

如果您在请求中传递的参数和请求头部都是正确的,并且仍然无法正常获取信息,那么可能是由于服务器端的问题
您的代码中在发送 GET 请求时使用了 data 参数来传递请求参数。通常,GET 请求中的参数是通过 URL 的查询字符串来传递的,而不是通过 data 参数。您可以使用 params 参数来设置查询字符串参数,如下所示:

# 构建请求参数(需要对参数进行URL编码)
params = {
    "PAGE": page,
    "PAGENUM": page_size
}

# 发送HTTP请求
response = requests.get(api_endpoint, headers=headers, params=params, timeout=(5,10))


该回答通过自己思路及引用到GPTᴼᴾᴱᴺᴬᴵ搜索,得到内容具体如下:
根据您提供的代码和问题描述,有几个可能导致您无法获取数据的原因和解决方法:

1、 请求参数问题:请确保您构建请求参数时进行了正确的URL编码。在您的代码中,您使用了data参数来传递请求参数,但根据您提供的问题描述,请求参数应该是通过URL进行传递的。请尝试使用params参数来设置请求参数,并对参数进行URL编码。

替换以下代码:

response = requests.get(api_endpoint, headers=headers, data=data, timeout=(5,10))

为:

response = requests.get(api_endpoint, headers=headers, params=data, timeout=(5,10))

2、 请求失败处理:在您的代码中,如果请求的状态码不是200,您只是简单地打印错误信息。建议您添加适当的错误处理机制,例如抛出异常或记录错误日志,以便更好地诊断请求失败的原因。

替换以下代码:

if response.status_code == 200:
    # 处理响应数据
else:
    print(f"请求失败,HTTP状态码: {response.status_code}")

为适合您的错误处理方式。

3、 调试信息:如果问题仍然存在,您可以在代码中添加更多的调试信息,以便了解请求的详细情况。例如,您可以打印出完整的请求URL、请求头、请求参数等,以便检查是否正确。

在以下代码之前添加打印语句:

print("Request URL:", response.url)
print("Request Headers:", response.request.headers)
print("Request Parameters:", response.request.params)

请尝试以上建议,如果问题仍然存在,请提供更多的错误信息和调试信息,以便我们更好地帮助您解决问题。


如果以上回答对您有所帮助,点击一下采纳该答案~谢谢

【以下回答由 GPT 生成】

我可以帮助你优化代码以确保成功调用接口并获取数据。首先,我会解释一下你提供的代码中的问题,并给出相应的解决方案。

在代码中,你定义了一些变量,如client_idclient_secretapi_endpoint等。然后,你创建了一个时间戳 timestamp,并初始化了一个CSV文件。但是在执行实际的HTTP请求时,你需要设置正确的请求头信息,包括X-Client-IdX-TimestampX-NonceX-Signature,以确保你的请求能够被正确识别和验证。

以下是一个优化的代码示例,用于调用政府数据公开接口:

import requests
import csv
import time
import hmac
import hashlib
import base64
import os
import random

client_id = "YOUR_CLIENT_ID"  # 你的客户端密钥
client_secret = "YOUR_CLIENT_SECRET"
api_endpoint = "http://data.sd.gov.cn/gateway/api/1/gpsfy"
output_directory = "YOUR_OUTPUT_DIRECTORY"  # 指定CSV输出目录

# 定义每次请求获取的数据数量和分批导出的数据数量
page_size = 50
export_batch_size = 5000000  # 每500万条数据导出一个CSV文件

# 计算总共需要多少次请求
total_records = 559249349  # 总记录数
total_pages = (total_records + page_size - 1) // page_size  # 总页数

# 准备CSV文件的列名
csv_columns = ["vehicle_no", "altitude", "add_time", "speed", "sim", "datetime", "lon", "state", "deviceno", "direction", "mileage", "lat"]

# 初始化CSV文件
batch_number = 0


# 生成X-Timestamp和X-Nonce
def generate_timestamp_and_nonce():
    timestamp = int(time.time() * 1000)  # 当前时间的毫秒数
    nonce = str(random.randint(100000, 999999))  # 生成一个6位的随机数
    return timestamp, nonce


# 生成X-Signature
def generate_signature(client_id, timestamp, nonce):
    signature_string = f"{client_id}{timestamp}{nonce}"
    signature = base64.b64encode(hmac.new(client_secret.encode(), signature_string.encode(), hashlib.sha256).digest())
    return signature.decode()


# 发送HTTP请求并获取数据
def send_request(url, headers):
    try:
        response = requests.get(url, headers=headers, timeout=(5, 10))
        response.raise_for_status()  # 如果请求失败,抛出异常
        return response.json()  # 返回JSON响应
    except requests.exceptions.RequestException as e:
        print(f"请求失败: {e}")
        return None


# 调用数据接口并获取数据
def get_data():
    for page in range(total_pages):
        # 生成请求头信息
        timestamp, nonce = generate_timestamp_and_nonce()
        signature = generate_signature(client_id, timestamp, nonce)
        headers = {
            "X-Client-Id": client_id,
            "X-Timestamp": str(timestamp),
            "X-Nonce": nonce,
            "X-Signature": signature
        }

        # 构建请求的URL和参数
        params = {
            "page": page,
            "size": page_size
        }

        url = f"{api_endpoint}?{urllib.parse.urlencode(params)}"

        # 发送HTTP请求并获取数据
        response_data = send_request(url, headers)

        if response_data:
            # 解析响应数据,并将数据写入CSV文件
            # 你可以根据实际的响应格式进行相应的解析
            # 这是一个示例,你需要根据实际情况进行修改
            csv_filename = f"data_batch_{batch_number}.csv"
            csv_filepath = os.path.join(output_directory, csv_filename)
            with open(csv_filepath, 'w', newline='') as csv_file:
                csv_writer = csv.DictWriter(csv_file, fieldnames=csv_columns)
                csv_writer.writeheader()
                csv_writer.writerows(response_data)

            batch_number += 1  # 更新批次号

            if batch_number % export_batch_size == 0:
                # 导出一个CSV文件后,重置批次号和CSV文件路径
                batch_number = 0
                csv_filename = f"data_batch_{batch_number}.csv"
                csv_filepath = os.path.join(output_directory, csv_filename)

        else:
            # 如果请求失败,则根据实际情况处理
            # 这里只是简单地打印错误信息
            print("请求失败,请检查网络连接或接口是否可用。")

这个优化的代码示例中添加了几个函数来生成请求头信息、发送请求和获取数据。首先,generate_timestamp_and_nonce 函数用来生成时间戳和随机数。然后,generate_signature 函数创建签名值。接下来,send_request 函数发送HTTP请求并返回响应数据。最后,get_data 函数循环调用数据接口并获取数据,同时将数据写入CSV文件。

请注意,响应数据的解析部分需要根据实际的响应格式进行修改,这只是一个示例。

希望这个解决方案能帮助到你,如果你还有其他问题,请随时提问。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^