博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python-定时爬取指定城市天气(一)-发送给关心的微信好友
阅读量:6374 次
发布时间:2019-06-23

本文共 7625 字,大约阅读时间需要 25 分钟。

一、背景

    上班的日子总是3点一线,家里,公司和上班的路径,对于一个特别懒得我来说,经常遇到上班路上下雨了,而我却没带伞,多么痛的领悟。最近对python有一种狂热的学习热情,写了4年多的C++代码,对于python我不能说简单,但是他做东西确实太快了,现有的第三方资源真的炒鸡多,用的我也是不亦乐乎。除了上班忘记带伞,每天重复性的工作还有很多,比如上下班打卡、每个礼拜的周报,还有如果有关心的女神,也可以做定时发送心里话,或者定时提醒等各种服务。有时候想如果有一个人能按时提醒我就好了,这种想法也就停留了那么几分钟就被自己pass掉了,因为别人也可能忘记啊。。。那么这件事是不是可以交给程序来做呢!毕竟程序可是会老老实实的做重复性的工作,而且他们乐此不疲。

    上述问题的场景大多都是需要程序在指定时间、或者指定场合提醒我们该干什么了,本篇文章就定时天气提醒服务来做开篇,讲述使用Python怎么完成这样一个任务,既然这样,那我们就开始构思我们的程序吧

二、构思

    看过背景中的需求描述,要实现这个功能,我们需要解决以下这么几个问题:

1、爬取天气信息,那么接下来就产生第二个问题了

2、动态获取指定城市天气

3、发送天气信息给指定微信好友

4、定时触发爬取动作

5、怎么关联微信账号

    下面我们将一步一步解决上述几个问题,并实现我们的需求

三、爬取天气

    解决问题1:

    对于使用过爬虫的同学来说,爬取天气信息并不难,之前也了解过一些爬取web信息的代码,简单的爬虫无非就是那么几步

1、确定爬取的url,使用浏览器打开

2、F12查看网页布局信息

3、使用xpath或者bs4进行节点定位

4、拿到页面信息

5、自己拼接爬取到的信息

6、写文件、写数据库、发送网络等等

    这里贴下我之前写的几个简单爬虫:

1、

2、

3、,这个应该还有个下篇,后面待续

    下面是爬取城市天气的python方法,需要注意一点的是getWeath接口的参数city_code,这是一个全国城市编码,每个城市都是唯一的,这个表格我已经整理成了一个txt文档,后续放源码的时候会一并提供。

1 headers = { 2         "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36", 3     } 4  5 def getWeath(city_code): 6     try: 7         url = f'http://www.weather.com.cn/weather/{city_code}.shtml' 8         resp = requests.get(url, headers = headers) 9     except BaseException as e:10         print(e)11         return {}12 13     resp.encoding = 'utf-8'14     soup = BeautifulSoup(resp.text, 'html.parser')15     tagToday = soup.find('p', class_ = "tem")  #第一个包含class="tem"的p标签即为存放今天天气数据的标签16     try:17         temperatureHigh = tagToday.span.string  #有时候这个最高温度是不显示的,此时利用第二天的最高温度代替。18     except AttributeError:19         temperatureHigh = tagToday.find_next('p', class_="tem").span.string  #获取第二天的最高温度代替20 21     temperatureLow = tagToday.i.string  #获取最低温度22     weather = soup.find('p', class_ = "wea").string #获取天气23     wind = soup.find('p', class_ = "win") #获取风力24     clothes = soup.find('li', class_ = "li3 hot") #穿衣指数25 26     return {
'温度':f'{temperatureHigh}/{temperatureLow}'27 , '天气':weather28 , '风力':wind.i.string29 , '穿衣':clothes.a.span.string + ',' + clothes.a.p.string}

     上述方法可以获取一个城市的天气信息,并储存在一个字典中,我们要发送给好友,还需要对其进行字符串处理,处理代码如下:

1 def strDic(dic):2     str_weather = ''3     for key in dic:4        str_weather += key + ':' + dic[key]5        str_weather += '\n'6     return str_weather

    全国城市编码如下图所示,每个城市的编码都是一个9位的数字组成,获取天气信息时是通过指定该编码进行查询。

 

四、发送给指定好友

    解决问题3:发送消息给好友

    解决问题5:怎么关联微信账号,使用wechat_sender库

    我们自己爬取到的天气信息怎么和微信能扯上关系呢,这个时候就要提到我之前写过的一篇文章,没有看过的同学可以快速浏览一遍,简单来说就是登陆一个web版本的微信账号,在我们的电脑上,做这么一个机器人使用了库,要想和这个机器人勾搭上,那我们就需要请出我们今天的重磅嘉宾,wechat_sender是基于和 实现的一个可以将你的网站,爬虫,脚本等其他应用中各种消息(日志,报警,运行结果等)发送到微信的工具包,有了他我们的消息就可以顺利的发送到我们的饿微信账户了。

交互流程

    如上图所示,首先使用wxpy登陆微信机器人,当然这个机器人使用的是我们自己的微信账号,这里需要特别注意一点,这篇文章中讲述的机器人进入命令状态是使用的embed()方法,在这里我们不能使用该接口了,我们需要换成上述交互流程的很关键的一步,使用listen接口进行监听,这样我们的web工具才能发送消息给机器人,建议仔细阅读一遍说明文档,内容不多

登陆微信机器人

    爬取到天气信息以后,使用wechat_sender中的Sender类直接发送消息给微信机器人,下属代码中尝试是用来多种发送消息的方式,代码中都有详细注释,可自行阅读

1 def sendWeatherMsg(receivers, msg): 2     try: 3         #receivers = [u'拉卡拉', u'证明给他看', u'李静'] 4          5         #receivers = u'李静,情绕指尖' 6           7         '''  8         #发送给指定好友 如果好友不存在 则发送给文件夹传输助手 9         Sender(receivers = u'证明给他看').send(msg)10         Sender(receivers = u'拉卡拉').send(msg)11         Sender(receivers = u'李静').send(msg)12         '''13 14         '''   '''15         #发送给指定接收的用户  16         #receivers = u'拉卡拉'17         #接受者必须是监听对象的子集18         sender = Sender(receivers = receivers, token = 'weather_report_123456789')19         sender.send(msg)#如果没有指定receivers则发送给文件传输助手20         21 22         ''' 23         receivers = u'李静,情绕指尖'24         sender = Sender(receivers = receivers, token = 'weather_report_123456789')25        26         #有时候好使    有时候不好使27         sender.send_to('@wss', u'拉卡拉') #消息发送失败 会默认发送给receivers的第一个用户 Sender和Listen28         #sender.send_to(msg, u'证明给他看')29         '''30 31         #测试控制命令32         '''33         receivers = u'拉卡拉'34         sender = Sender(receivers = receivers, token = 'weather_report_123456789')35         sender.send('@wss')#文如果没有指定receivers则发送给文件传输助手件传输助手36         '''37 38     except BaseException as e:39         print(e)

    登陆微信机器人已经讲过,有不懂的同学可以回头看下,下边代码中第12行非常关键,这一行就是用来监听外部程序发送消息的。

1 bot = Bot(cache_path = True) 2  3 receivers = [] 4 receivers.append(bot.file_helper) 5 receivers.append(bot.friends().search('拉卡拉')[0]) 6 receivers.append(ensure_one(bot.friends().search('李静', city='西安')))#有可能搜索出多个结果 7 receivers.append(bot.friends().search('证明给他看')[0]) 8 receivers.append(bot.friends().search('妈')[0]) 9 10 print(receivers)11 12 listen(bot, receivers = receivers, token = 'weather_report_123456789') #关键一步

五、城市编码

    解决问题2,根据配置的城市名称动态获取城市编码,然后请求数据

    由于没有接口可以直接获取城市编码,因此这里我们自己封装了一个类来进行管理城市名称和城市编码,拉取城市天气时,只要输入城市名称,那么城市编码即可通过该类获取到,具体代码如下

1 import os 2  3 class City(object): 4     def __init__(self): 5         self.city = {} 6  7     def load(self, file): 8         if os.path.exists(file): 9             with open(file, 'r', encoding = 'utf-8') as f:10                 cityInfo = f.readline().strip('\n')11                 while cityInfo:12                     datas = cityInfo.split(':')13                     self.city[datas[0]] = datas[1]14                     cityInfo = f.readline().strip('\n')15 16     def find_code(self, city_name):#根据城市名称,查找城市吧编码17         if city_name in self.city:18             return self.city[city_name]19         return ''

六、定时任务 

    解决问题4:定时发送任务

    我们的需求是每日定时拉取天气信息,并发送给指定好友,python有一个APScheduler库,支持定时任务,具体使用比较负责,我也没有仔细研究,这里我们只是需要使用一个定时任务,其他不做介绍,有兴趣的同学可自行研究。

    在研究定时任务的过程中,一直没有找到BackgroundScheduler类add_job时,回调函数怎么传递参数,因此这里我封装了一个类,让定时任务和任务回调处于一个域内,这样参数就可以放在类的成员变量未知,不需要传递了,哪位大神如果会次操作,可以评论区指出,非常感谢

1 class MyJob(object): 2     def __sendWeatherMsg(self): 3         for my_job in self.my_jobs: 4             code = city_code.find_code(my_job['city']) 5             wea = getWeath(code) 6             strWea = strDic(wea) 7             title = '{}天气预报:\n'.format(my_job['city']) 8             sendWeatherMsg(my_job['receivers'], title + strWea)#发送天气信息给文件助手 9 10     def addMyJobs(self, json_job):11         self.my_jobs = json_job['items']12         scheduler = BackgroundScheduler()13         scheduler.add_job(self.__sendWeatherMsg, trigger = 'cron', hour = json_job['hour']14         , minute = json_job['minute'], second = '5,10,15,20,25,30,35,40,45,50,55')15         scheduler.start()

    后期出现不同类型任务时,我们就需要在封装新的类。上述MyJob类有2个接口,一个是任务调度器回调接口,不需要我们调用,另一个是加载任务接口,这个任务参数是一个标准的json串,由任务触发时间和具体的任务列表组成,任务触发时间主要是给调度器使用,任务列表就是调度器触发时的回调函数需要执行的任务数量。

1 my_jobs = { 2     "id":"my_jobs", 3     "hour":"6, 17", 4     "minute":"30", 5     "items":[{ 6     "receivers":"文件传输助手,李静,拉卡拉", 7     "city":"昌平" 8     },{ 9     "receivers":"文件传输助手,李静,拉卡拉",10     "city":"海淀"11     }]12 }

    如上述任务json串来说,我们的任务id为my_jobs,在每天的6.30和17.30,我们需要执行items列表所指出的任务,任务列表是一个列表,列表中存储的是具体任务,receivers代表任务执行完毕需要发送的好友,city是爬取的天气名称,测试效果如下图所示

    由于任务调度器不是一个阻塞性的程序,如果我们不在主线程进行阻塞程序,那么程序就会直接退出,如果阻塞了主线程,那么任务调度程序也将会被阻塞,因此这里在添加任务调度后,我们开启了一个子线程,主要就是为了不让主线程退出,这样做其实不合理,但是我们这里仅仅是为了演示,在下篇文章中这些问题我们在做进一步处理。

1 city_code = city_code.City() 2 city_code.load('city_code.txt')  3  4 if __name__ == "__main__": 5     try: 6         ''' ''' 7         my_job = MyJob() 8         my_job.addMyJobs(test_jobs) 9 10         f = lambda x : lambda y : x+y11         t = Timer.Timer(f, 24 * 60 * 60)#创建线程 一天给自己发一条消息12         t.setDaemon(True)13         t.start()14         t.join()  #防止主主线程退出15         16         #SendWeatherMsg(my_msg)17 18     except ResponseError as e:19         print(e.err_code, e.err_msg) # 查看错误号和错误消息

    喜欢的同学可以自己尝试完成下这个小程序,或者选择一个类似的场景进行处理,本篇文章中还有几个需要优化的地方,由于篇幅问题,我们在下篇中进行讲解

1、定时任务做成windows服务,这样更优雅,随开机启动

2、发送消息给微信好友换成发送邮件给指定邮箱

七、资源下载 

    需要全部代码的到csdn直接下载:

 

转载声明:本站文章无特别说明,皆为原创,版权所有,转载请注明:

你可能感兴趣的文章
java io教学文件_通过代码实例跟我学Java语言程序设计及应用技术——Java文件IO技术及应用相关的教学示例(第4部分)...
查看>>
JAVA匹配所有英文_java匹配汉字、英文、数字
查看>>
java_语句姓名_密码_JAVA程序设计项目报告书.doc
查看>>
mysql5.17导出数据库_mysql导出数据库文档
查看>>
os x查询java版本_在OS X管理多个Java版本
查看>>
dockerfile 安装 java_docker第一天:window10下docker采用Dockerfile安装jdk1.8
查看>>
realm java 0.9.0_java-找不到io.realm:realm-gradle-plugin:4.0.0.pr...
查看>>
python关键词对联_深度学习对联系统
查看>>
java jsp不显示数据库数据_jsp页面显示数据库的数据信息表
查看>>
java中project_Java Project引用Java project
查看>>
mac nginx php-fpm,Mac系统下搭建Nginx+php-fpm
查看>>
java策略模式使用场景,Java设计模式—策略模式
查看>>
oracle时钟,oracle RAC环境中系统时钟的调整
查看>>
oracle数据库体系结构ppt,Oracle数据库的体系结构
查看>>
linux 虚拟目录映射,IIS7添加虚拟目录映射另一台服务器的共享文件夹
查看>>
linux杀一个awk查出的进程,Linux shell - 找到进程pid,然后杀掉(jps, grep, awk)
查看>>
linux内核模块获取进程pid,内核模块打印进程线程PID信息
查看>>
linux共享文件夹sumba,linux 的 samba 实现共享文件夹
查看>>
linux查看设备使用状况,linux下查看硬件资源和网络资源的使用情况
查看>>
centos linux终端,CentOS7 Linux系统命令
查看>>