Python调用HaveIBeenPwned API遇TypeError及数据遍历问题求助
解决HaveIBeenPwned API调用的几个问题
我来帮你一步步搞定遇到的这些问题:
1. 先解决 TypeError: string indices must be integers 错误
这个错误的核心是你把字符串当成字典来访问了,通常发生在API返回非预期响应的时候——比如当邮箱没有泄露记录时,HIBP API会返回404状态码,响应内容不是你预期的JSON数组,这时候强行解析后得到的是字符串,再用['Title']这种字典索引去访问就会触发错误。
你需要先检查API的响应状态,处理非200的情况:
def DataGetCurrent(Username): SearchURL = "https://haveibeenpwned.com/api/v2/breachedaccount/{}".format(Username) try: request = url.urlopen(url.Request(SearchURL, headers={'User-Agent' : "Mozilla/5.0"})) # 先确认响应正常 if request.getcode() != 200: return None data = request.read().decode("utf-8") json_data = json.loads(data) return json_data # 返回整个结果数组,不是第一个元素 except Exception as e: print(f"请求出错了: {str(e)}") return None
2. 遍历所有泄露条目并统计数量
之前你只返回了json_data[0],自然只能拿到第一个条目。现在我们返回整个数组,然后遍历它就能统计数量并输出所有内容:
修改EmailSearch函数:
def EmailSearch(): email = input("Please enter the Email that's going to be searched \n: ") data_list = DataGetCurrent(email) if not data_list: print("你的邮箱没有被泄露的记录哦。") return # 统计泄露次数 breach_count = len(data_list) print(f"你的邮箱一共涉及 {breach_count} 次数据泄露:\n") # 遍历所有泄露条目 for idx, breach in enumerate(data_list, 1): # 处理DataClasses列表,转成易读的字符串 leaked_data = ", ".join(breach['DataClasses']) print(f"=== 第 {idx} 次泄露 ===") print(f"标题: {breach['Title']}") print(f"网站: {breach['Domain']}") print(f"泄露日期: {breach['BreachDate']}") print(f"泄露详情: {breach['Description']}") print(f"泄露的数据类型: {leaked_data}\n")
3. 正确提取和打印DataClasses
DataClasses是一个数组(比如['Email addresses', 'Passwords']),直接打印会显示列表格式,用", ".join(breach['DataClasses'])就能把它转换成逗号分隔的友好格式。
完整修改后的代码
把这些修改整合后,最终代码如下:
# This program aims to provide 4 search functions by which users can test if their data is at risk. import urllib.request as url import json def UsernameSearch(): print("已选择用户名搜索功能!") def PasswordSearch(): print("已选择密码搜索功能!") def EmailSearch(): email = input("请输入要查询的邮箱地址 \n: ") def DataGetCurrent(username): SearchURL = "https://haveibeenpwned.com/api/v2/breachedaccount/{}".format(username) try: request = url.urlopen(url.Request(SearchURL, headers={'User-Agent' : "Mozilla/5.0"})) if request.getcode() != 200: return None data = request.read().decode("utf-8") return json.loads(data) except Exception as e: print(f"请求出错: {str(e)}") return None data_list = DataGetCurrent(email) if not data_list: print("你的邮箱没有被泄露的记录。") return breach_count = len(data_list) print(f"你的邮箱一共涉及 {breach_count} 次数据泄露:\n") for idx, breach in enumerate(data_list, 1): leaked_data = ", ".join(breach['DataClasses']) print(f"=== 第 {idx} 次泄露 ===") print(f"标题: {breach['Title']}") print(f"网站: {breach['Domain']}") print(f"泄露日期: {breach['BreachDate']}") print(f"泄露详情: {breach['Description']}") print(f"泄露的数据类型: {leaked_data}\n") def SiteSearch(): print("已选择网站搜索功能!") def loop(): try: answer = input("当前有5个选项: \n(1)用户名搜索 \n(2)密码搜索 \n(3)邮箱搜索 \n(4)网站搜索 \n(5)退出 \n \n:") answer_clean = answer.strip().upper() if answer_clean == "1": UsernameSearch() elif answer_clean == "2": PasswordSearch() elif answer_clean == "3": EmailSearch() elif answer_clean == "4": SiteSearch() elif answer_clean == "5" or answer_clean == "EXIT": print("正在退出程序...") return else: print("\n输入无效,请重新尝试!\n") loop() except KeyboardInterrupt: print("\n不用这么急着退出啦,输入\"exit\"或者\"5\"就能正常退出程序!\n") loop() loop()
额外优化的小细节
- 把变量名
Username改成了email,更符合语义; - 给退出选项增加了对"EXIT"和"5"的支持,用户操作更方便;
- 增加了异常捕获,处理网络波动等可能的请求错误;
- 用
enumerate给每个泄露条目加上序号,输出更清晰。
内容的提问来源于stack exchange,提问作者JohnSmith
相关产品推荐
相关产品推荐

