欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

从零开始用Python做一个女朋友 • 第一弹

程序员文章站 2024-03-12 16:55:14
...

从零开始用Python做一个女朋友 • 第一弹

因为一只蝙蝠所引起的一系列连锁反应,导致一直宅在家中的自己突然有了重拾Python的勇气,一直以来想要做个智能对话机器人,于是说干就干,从最简单的关键词匹配做起,在目前进度中,通过调用公共接口实现了天气查询和实时新闻查询两个功能,后续功能将逐渐完善。由于本人Python学识不深,很多细节的处理上可能不尽完善,欢迎各位大佬指点~

由于目标是比较智能的机器人,所以借用了动漫刀剑的梗虚拟人物形象叫Alice 【宅属性暴露无遗】



总体思路还是模块化的思想,通过循环接收用户输入,然后通过自定义的__analyse()函数进行关键词匹配,将匹配结果通过数组返回,并在主过程内遍历数组内的事件标签,如果天气查询标签或新闻查询标签在内,则将用户输入传递给相应的自定义模块函数进行处理。话不多说,先呈上main.py模块代码:

# -*- coding:utf-8 -*-
import weather  #自定义天气查询处理模块
import news     #自定义新闻查询处理模块

def __analyse(raw_str):
    analyse_result = []
    weather_wors = ['天气', 'weather']
    news_words = ['新闻', 'news', "头条"]
    if any(word in raw_str for word in weather_wors):
        analyse_result.append('weather')    #为返回的结果数组添加'weather'标签
    if any(word in raw_str for word in news_words):
        analyse_result.append('news')       #为返回的结果数组添加'news'标签
    return analyse_result

if __name__ == '__main__':
    while (True):
        raw_str = input()
        analyse_result = __analyse(raw_str)
        if 'weather' in analyse_result:
            weather.handle(raw_str)     #调用自定义天气查询处理模块的处理函数
        if 'news' in analyse_result:
            news.handle(raw_str)        #调用自定义新闻查询处理模块的处理函数
        else:
            print('Alice没能听懂主人说的是什么哇……')

下面以weather.py为例解释天气查询内部的处理过程(news.py模块同理):
自定义模块的handle()函数在接收到用户输入后,也是首先调用本模块内的__analyse()函数进行关键词匹配,与main模块内不同的是,由于返回的结果除去标签还需要一部分属性值,譬如说天气查询的地点、时间等,所以定义了Tag便签对象,用来作为返回的标签数组的元素。

class Tag:
    name = ''       #标签名
    props = {}      #属性值

    def __init__(self, name, props):
        self.name = name
        self.props = props

为了更加方便地修改定制,我增加了读取目录下配置文件config.ini获取默认值及查询接口API的功能,相关代码如下:

import configparser     #可以通过 pip install 进行安装
import os

__config = configparser.ConfigParser()
__config.read(os.getcwd() + "/config.ini", encoding='utf-8')

__weather_api_url = __config.get("weather", "api")      #天气查询API链接
__weather_api_key = __config.get("weather", "key")      #天气查询API的个人**
__weather_default_city = __config.get("weather", "default_city")    #在未指定查询城市时默认的查询城市
__temp_threshold = __config.get("weather", "temp_threshold")        #温差提醒阈值
__wind_threshold = __config.get("weather", "wind_threshold")        #风力提醒阈值

config.ini文件的内容如下:

[weather]
; 天气查询API
api=http://apis.juhe.cn/simpleWeather/query
; 天气查询默认城市
default_city=长沙
; 天气查询API**
key=***************
; 温差提醒阈值
temp_threshold=15
; 风力提醒阈值
wind_threshold=6

天气查询及新闻查询API使用来源于 [聚合数据] ,个人作为开发者注册认证之后每天可以免费调用100次,个人使用完全足够了,在右上角“个人中心” -> “数据中心” -> “我的接口”内可以搜索和申请接口,接口申请成功后会自动分配一个Key,之后每次调用时需要携带这个Key进行访问。
从零开始用Python做一个女朋友 • 第一弹
从零开始用Python做一个女朋友 • 第一弹
点击图二右侧“测试”按钮可以进入测试页面,网页将模拟真实调用的情形对请求参数以及返回数据进行展示,为了辅助接下来的代码展示,先展示一下某次查询链接以及返回的json数据:

http://apis.juhe.cn/simpleWeather/query?city=%E9%95%BF%E6%B2%99&key=************
// 注意此处的city参数必须为 utf8 urlencode 形式
{
	"reason":"查询成功",
	"result":{
		"city":"长沙",
		"realtime":{
			"temperature":"14",
			"humidity":"73",
			"info":"阴",
			"wid":"02",
			"direct":"西南风",
			"power":"2级",
			"aqi":"55"
		},
		"future":[
			{
				"date":"2020-02-22",
				"temperature":"11\/18℃",
				"weather":"多云",
				"wid":{
					"day":"01",
					"night":"01"
				},
				"direct":"北风转南风"
			},
			{
				"date":"2020-02-23",
				"temperature":"11\/22℃",
				"weather":"晴转多云",
				"wid":{
					"day":"00",
					"night":"01"
				},
				"direct":"南风"
			},
			{
				"date":"2020-02-24",
				"temperature":"15\/26℃",
				"weather":"多云",
				"wid":{
					"day":"01",
					"night":"01"
				},
				"direct":"南风"
			},
			{
				"date":"2020-02-25",
				"temperature":"17\/25℃",
				"weather":"阵雨",
				"wid":{
					"day":"03",
					"night":"03"
				},
				"direct":"南风"
			},
			{
				"date":"2020-02-26",
				"temperature":"12\/23℃",
				"weather":"阵雨",
				"wid":{
					"day":"03",
					"night":"03"
				},
				"direct":"北风"
			}
		]
	},
	"error_code":0
}

然后接下来就是weather.py模块中文本分析函数__analyse()的实现了,为了方便阅读,此处将支持从城市关键词数组city_words只展示一部分,所有支持查询的城市大家也可以在 [聚合数据] 上进行查询,该网站也提供了查询支持的城市的API:

def __analyse(raw_str):
    analyse_result = []
    weather_query_words = ['?', '怎么样', '如何']
    weather_talk_words = ['不错', '喜欢', '不喜欢']
    city_words = ['北京', '海淀', '朝阳', '顺义', '怀柔', '通州', '昌平', '延庆', '丰台', '石景山', '大兴', '房山', '密云', '门头沟', '平谷', '上海']       #支持查询的城市关键词
    if any(word in raw_str for word in weather_query_words):
        query_props = {'time': 'today', 'city': __weather_default_city}     #如果未指定查询时间和城市则默认查询今天以及config.ini配置文件中城市的天气
        today_words = ['今天', 'today']         #查询时间为“今天”的关键词
        tomorrow_words = ['明天', 'tomorrow']   #查询时间为“明天”的关键词
        twodays_words = ['今天和明天', '今明']  #查询时间为“今明两天”的关键词
        if any(word in raw_str for word in today_words):
            query_props['time'] = 'TODAY'
        if any(word in raw_str for word in tomorrow_words):
            query_props['time'] = 'TOMORROW'
        if any(word in raw_str for word in twodays_words):
            query_props['time'] = 'TWODAYS'
        for city in city_words:
            if city in raw_str:
                query_props['city'] = city
                break
        analyse_result.append(Tag('weather-query', query_props))
    if any(word in raw_str for word in weather_talk_words):
        analyse_result.append(Tag('weather-talk', None))
    return analyse_result

__analyse()调用分析之后,接下来就是handle()函数进行处理的部分了:

def handle(raw_str):
    analyse_result = __analyse(raw_str)     #对主过程传入的raw_str进行关键词分析
    for tag in analyse_result:
        if tag.name == 'weather-talk':
            print('今天的天气真的很不错哦')
        if tag.name == 'weather-query':
            weather_complete_api = __weather_api_url + "?city=" + quote(
                tag.props["city"]) + "&key=" + __weather_api_key        #调用quote函数将城市名转为 utf8 urlencode 形式并拼凑URL
            weather_response = json.loads(requests.get(weather_complete_api).content)       #调用天气查询API并获取返回JSON格式的结果
            error_code = weather_response['error_code']
            #处理异常状态
            if (error_code == 207301 or error_code == 207302):
                print("Alice不知道这座城市是哪里的呢……要不主人再换一个试试?")
            elif (error_code == 207303):
                print("哔——电波在传输过程中好像出了点问题哎……")
            elif (error_code == 0):        #错误码为0代表查询成功
                print("嘀哩哩~数据接收完成,下面为主人播报天气——")
                city = weather_response["result"]["city"]       #返回的JSON内反馈的查询结果城市
                info_today = weather_response["result"]["future"][0]["weather"]         #今日的天气状况
                temperature_today = weather_response["result"]["future"][0]["temperature"]      #今天的最低气温和最高气温
                temperature_today_min = temperature_today[:temperature_today.find('/')]     #处理temperature_today获得今日最低气温
                temperature_today_max = temperature_today[temperature_today.find('/') + 1:temperature_today.find('℃')]      #处理temperature_today获得今日高气温
                info_tomorrow = weather_response["result"]["future"][1]["weather"]      #明日天气状况
                temperature_tomorrow = weather_response["result"]["future"][1]["temperature"]       #明日的最低气温和最高气温
                temperature_tomorrow_min = temperature_tomorrow[:temperature_tomorrow.find('/')]        #处理temperature_today获得明日最低气温
                temperature_tomorrow_max = temperature_tomorrow[
                                           temperature_tomorrow.find('/') + 1:temperature_tomorrow.find('℃')]       #处理temperature_today获得明日高气温
                wind_direction_today = weather_response["result"]["future"][0]["direct"]        #今日风向
                wind_direction_tomorrow = weather_response["result"]["future"][1]["direct"]     #明日风向
                wind_power_now = weather_response["result"]["realtime"]["power"]                #当前时间风力大小
                #对标签内各属性值进行匹配处理
                if tag.props['time'] == 'TODAY':
                    response_text = "今天[" + city + "]天气" + info_today + ",气温 " + temperature_today + ",今天风向为" + wind_direction + ",当前风力" + wind_power_now + "。"
                    if (int(temperature_today_max) - int(temperature_today_min) >= int(__temp_threshold)):
                        response_text = response_text + "\n今天昼夜温差很大的说,主人要多加注意穿衣保暖哦~"
                    if (int(wind_power_now[:-1]) >= int(__wind_threshold)):
                        response_text = response_text + "\n另外,现在的风也有点大呢,主人一定当心不要被风吹跑啦,嘻嘻~"
                    print(response_text)
                if tag.props['time'] == 'TOMORROW':
                    response_text = "明天[" + city + "]天气" + info_tomorrow + ",气温 " + temperature_tomorrow + ",风向为" + wind_direction_tomorrow + "。"
                    if (int(temperature_tomorrow_max) - int(temperature_tomorrow_min) >= int(__temp_threshold)):
                        response_text = response_text + "\n明天昼夜温差很大的说,主人要多加注意穿衣保暖哦~"
                    print(response_text)
                if tag.props['time'] == 'TWODAYS':
                    response_text = "今天[" + city + "]天气" + info_today + ",气温 " + temperature_today + ",今天风向为" + wind_direction_today + ",当前风力" + wind_power_now + ";\n明天天气" + info_tomorrow + ",气温 " + temperature_tomorrow + ",风向为" + wind_direction_tomorrow + "。"
                    if (int(temperature_today_max) - int(temperature_today_min) >= int(__temp_threshold) or int(
                            temperature_tomorrow_max) - int(temperature_tomorrow_min) >= int(__temp_threshold)):
                        response_text = response_text + "\n今明两天昼夜温差很大的说,主人要多加注意穿衣保暖哦~"
                    if (int(wind_power_now[:-1]) >= int(__wind_threshold)):
                        response_text = response_text + "\n另外,现在的风也有点大呢,主人一定当心不要被风吹跑啦,嘻嘻~"
                    print(response_text)
            else:
                print("发生了Alice也不清楚的错误呢(๑Ő௰Ő๑)……要不主人帮忙看一下\n错误代码:[Weather-API:" + error_code + "]")     #对于API的系统性错误直接抛出错误代码处理

由于使用了文字转码以及JSON处理等操作,weather.py需要导入的全部包如下:

from urllib.parse import quote
import configparser
import requests
import json
import os

最后看一下实战效果吧:
从零开始用Python做一个女朋友 • 第一弹

目前实现的功能模块细节就如上所述啦,后序会添加更多的非功能性的模块,譬如情感处理以及好感度等模块,欢迎有新意的脑洞,[GitHub源码]