起因

我在github上发起了一个开源项目:《HelloGitHub月刊》,内容是github上收集的好玩,容易上手的开源项目。

目的:因为兴趣是最好的老师,我希望月刊中的内容可以激发读者的兴趣,从而动手参与到开源的项目中,一方面提高编程技术、另一方面哪怕是能力有限不能为开源项目提交代码,也可以给个‘star’,表示对有意思、优秀的开源项目的支持!从而让开源社区越来越好。

所以,我就需要收集github上的开源项目,目前通过两种方式发现github上优秀的项目:

  1. Follow活跃的Github用户,收集他们Starred的项目
  2. Github的Explore页

然后,我就想能不能写个脚本,每天跑一次把这两个数据源的数据,收集整理好,然后发到我的邮箱中。这个需求很简单,初步感觉就两个问题:

  1. 数据源的api
  2. 发邮件的方法

过程

数据源

Github API提供了诸多获取Github数据的接口:

  1. List public events that a user has receivedGET /users/:username/received_events/public,这个接口返回user的动态(包含user关注的用户、项目的动态)
  2. 暂时没找到Explore页的接口,如果实在找不到,我就尝试爬取。

代码

完整的代码我的Github

1、请求api:

首选requests库,真的是居家旅行必备良品。需要注意一点,请求GET /users/:username/received_events/public需要用户验证,请求api的函数如下:

  1. def get_data(page=1, per_page=100):
  2. """
  3. 从目标源获取数据
  4. """
  5. args = '?page={page}&per_page={per_page}'.format(
  6. page=page, per_page=per_page)
  7. response = requests.get(API['events']+args,
  8. auth=(ACCOUNT['username'], ACCOUNT['password']))
  9. status_code = response.status_code
  10. if status_code == 200:
  11. resp_json = response.json()
  12. return resp_json
  13. else:
  14. logging.error('请求api失败:', status_code)
  15. return None
2、根据条件过滤数据:

请求api回来的json数据如下:

  1. [
  2. ...
  3. {
  4. "id": "4123123423",
  5. "type": "WatchEvent",
  6. "actor": {
  7. "id": 12342134,
  8. "login": "gera2ld",
  9. "display_login": "gera2ld",
  10. "gravatar_id": "",
  11. "url": "https://api.github.com/users/gera2ld",
  12. "avatar_url": "https://avatars.githubusercontent.com/u/3139113?"
  13. },
  14. "repo": {
  15. "id": 23412431,
  16. "name": "yahoo/gifshot",
  17. "url": "https://api.github.com/repos/yahoo/gifshot"
  18. },
  19. "payload": {
  20. "action": "started"
  21. },
  22. "public": true,
  23. "created_at": "2016-09-03T16:25:34Z",
  24. "org": {
  25. "id": 234234,
  26. "login": "yahoo",
  27. "gravatar_id": "",
  28. "url": "https://api.github.com/orgs/yahoo",
  29. "avatar_url": "https://avatars.githubusercontent.com/u/16574?"
  30. }
  31. },
  32. {
  33. "id": "234234",
  34. "type": "WatchEvent",
  35. "actor": {
  36. "id": 21341234,
  37. "login": "phith0n",
  38. "display_login": "phith0n",
  39. "gravatar_id": "",
  40. "url": "https://api.github.com/users/phith0n",
  41. "avatar_url": "https://avatars.githubusercontent.com/u/5711185?"
  42. },
  43. "repo": {
  44. "id": 23234,
  45. "name": "yummybian/ThreadPool",
  46. "url": "https://api.github.com/repos/yummybian/ThreadPool"
  47. },
  48. "payload": {
  49. "action": "started"
  50. },
  51. "public": true,
  52. "created_at": "2016-09-03T16:12:56Z"
  53. }
  54. ]
  55. ...

分析上面的json数据,其中可能会包含我不需要的信息(非starred事件的数据)需要过滤掉、同时需要根据时间获取某一段时间的数据。比如我写的这个github bot脚本获取24个小时的数据,我设定脚本每天凌晨4点跑——例如:9.3 4:00——9.4 4:00(24h的数据)。下面写了两个函数,用于过滤符合条件的数据:

注意: 接口返回的数据中的create_at字段的时间值形如——created_at: "2016-09-03T16:12:56Z" 是‘协调世界时’,‘Z’是协调世界时中0时区的标志,北京是8时区,所以就是需要在这个时间的基础上+8小时。这个事件发生于北京时间:"2016-09-04 00:12:56"

  1. def check_condition(data):
  2. """
  3. 过滤条件
  4. """
  5. create_time = datetime.datetime.strptime(
  6. data['created_at'], "%Y-%m-%dT%H:%M:%SZ") + datetime.timedelta(hours=8)
  7. date_condition = create_time >= (datetime.datetime.now()
  8. - datetime.timedelta(days=DAY))
  9. if (data['type'] == 'WatchEvent') and date_condition:
  10. if data['payload']['action'] == 'started':
  11. data['date_time'] = create_time.strftime("%Y-%m-%d %H:%M:%S")
  12. return True
  13. else:
  14. return False
  15. def analyze(json_data):
  16. """
  17. 分析获取的数据
  18. :return 符合过滤条件的数据
  19. """
  20. result_data = []
  21. for fi_data in json_data:
  22. if check_condition(fi_data):
  23. result_data.append(fi_data)
  24. return result_data
3、生成发送邮件的内容:

最终邮件内容如下:

注意: 因为获取项目stars数的接口,有的时候获取数据很慢,所以设置了超时时间。最好的方法因该是以异步的方式解决。可以参考grequests项目

  1. CONTENT_FORMAT = """
  2. <table border="2" align="center">
  3. <tr>
  4. <th>头像</th>
  5. <th>用户名</th>
  6. <th>项目名</th>
  7. <th>starred日期</th>
  8. <th>项目star数量</th>
  9. </tr>
  10. {starred_info}
  11. </table>
  12. """
  13. def make_content():
  14. """
  15. 生成发布邮件的内容
  16. """
  17. json_data = get_data()
  18. data = analyze(json_data)
  19. content = []
  20. for fi_data in data:
  21. user = fi_data['actor']['login']
  22. user_url = 'https://github.com/' + user
  23. avatar_url = fi_data['actor']['avatar_url']
  24. repo_name = fi_data['repo']['name']
  25. repo_url = 'https://github.com/' + repo_name
  26. date_time = fi_data['date_time']
  27. try:
  28. repo_stars = requests.get(fi_data['repo']['url'], timeout=2).json()
  29. if repo_stars:
  30. repo_stars = repo_stars['stargazers_count']
  31. else:
  32. repo_stars = '未知数'
  33. except Exception as e:
  34. repo_stars = '未知数'
  35. logger.warning(u'获取:{} 项目星数失败——{}'.format(repo_name, e))
  36. starred_info = """<tr>
  37. <td><img src={avatar_url} width=32px></img></td>
  38. <td><a href={user_url}>{user}</a></td>
  39. <td><a href={repo_url}>{repo_name}</a></td>
  40. <td>{date_time}</td>
  41. <td>{repo_stars}</td>
  42. </tr>
  43. """.format(user=user, repo_name=repo_name,
  44. repo_url=repo_url, user_url=user_url,
  45. avatar_url=avatar_url, repo_stars=repo_stars,
  46. date_time=date_time)
  47. content.append(starred_info)
  48. return content
4、发送邮件:

如果是使用qq邮箱发送邮件,可以参考:qq邮件服务文档

注意: 发送邮件使用的邮箱密码,最好用授权码,因为我在测试邮件发送功能时,发送邮件的次数太多,后面突然不能发送了!而且没有任何错误提示,就卡在sendmail方法!后来登录qq邮箱,发现让我使用授权码 进行第三方授权。最后使用授权码一切就ok了!

发送邮件的函数如下:

  1. def send_email(receivers, email_content):
  2. """
  3. 发送邮件
  4. """
  5. sender = MAIL['mail'] # 发送邮件的邮箱
  6. receivers = receivers # 接收邮件的邮箱,可设置多个
  7. # 三个参数:第一个为文本内容,第二个 html 设置文本格式,第三个 utf-8 设置编码
  8. message = MIMEText(
  9. CONTENT_FORMAT.format(starred_info=''.join(email_content)),
  10. 'html', 'utf-8'
  11. )
  12. message['From'] = Header(u'Github机器人', 'utf-8')
  13. message['To'] = Header(u'削微寒', 'utf-8')
  14. subject = u'今日Github热点' # 设置邮件主题
  15. message['Subject'] = Header(subject, 'utf-8')
  16. try:
  17. smtp_obj = smtplib.SMTP_SSL() # qq邮箱要求是https连接,所以需要用SMTP_SSL
  18. smtp_obj.connect(MAIL['host'], MAIL['port']) # 设置SMTP地址和端口号
  19. smtp_obj.login(MAIL['username'], MAIL['password'])
  20. smtp_obj.sendmail(sender, receivers, message.as_string())
  21. except smtplib.SMTPException as e:
  22. logger.error(u"无法发送邮件: {}".format(e))

完整的代码我的Github

最后

参照crontab 定时任务,在linux下设置定时任务。

  1. 1. EDITOR=vi; export EDITOR #使用vi编辑器编辑
  2. 2. crontab -e #加入定时任务
  3. 3. crontab -l #查看是否加入成功

TODO

  1. 获取Explore页的数据

参考

《HelloGitHub》之GitHub Bot的更多相关文章

  1. 《HelloGitHub》第 75 期

    兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 HelloGitHub 分享 GitHub 上有趣.入门级的开源项目. https://github.com/521xueweiha ...

  2. GitHub 热点速览 Vol.13:近 40k star 计算机论文项目再霸 GitHub Trending 榜

    作者:HelloGitHub-小鱼干 摘要:"潮流是个轮回",这句话用来形容上周的 GitHub Trending 最贴切不过.无论是已经获得近 40k 的高星项目 Papers ...

  3. GitHub 热点速览 Vol.14:周获 2k+ Vim 掀起三维编程风

    作者:HelloGitHub-小鱼干 摘要(用于 公众号/博客园等地方)寓教于乐,应该是上周 Trending 的主题了,无论是被多人转发推荐的三维 Vim 项目 Vim³ 或者是流体运动的 WebG ...

  4. GitHub 热点速览 Vol.15:Background-Matting 让你秒变专业抠图师

    作者:HelloGitHub-小鱼干 摘要:如果要选一个词来概述上周的热点,春风拂过,应该是一个不错的词.新项目像春天冒出的枝芽,朝气蓬勃,虽然获得的 star 不如之前三维 Vim 抢眼,但胜在多姿 ...

  5. GitHub 热点速览 Vol.16:化身蒙娜丽莎和乔布斯对话

    摘要:妙趣横生,上周的 GitHub 热点的关键词.无论是让你化身为爱因斯坦开启会议脑暴模式 avatarify,还是和上周人人都是抠图师项目的同门项目 3D 照片修复:3d-photo-inpain ...

  6. GitHub 热点速览 Vol.17:在?各家视频会员要不要?

    作者:HelloGitHub-小鱼干 摘要:经济实用,用作上周的 GitHub 热点的横批再合适不过.先不说 GitHub Trending 上不止一个的会员共享项目,免你找好友刷脸要会员,这项目实在 ...

  7. GitHub 热点速览 Vol.18:刷 LeetCode 的正确姿势

    作者:HelloGitHub-小鱼干 摘要:找对路子,事半功倍,正如本周 GitHub Trending #刷 LeetCode# 主题想表达的那般,正确的学习姿势方能让人走得更远,走进大厂

  8. GitHub 热点速览 Vol.19:如何叩响大厂的门?

    作者:HelloGitHub-小鱼干 摘要:进大厂,无疑是升职加薪走上人生巅峰的一个敲门砖,那,如何拿到这个敲门砖呢?前辈的经验之谈,无疑会给我们进大厂带来许多的经验参考,本周的#大厂面试经验之谈#主 ...

  9. GitHub 热点速览 Vol.20:VSCode 插件全家桶新增画图小能手

    作者:HelloGitHub-小鱼干 摘要:后浪,这个五月热词用来概括 GitHub 本周热点无疑是最佳词汇.Deno 这个 Node.js 作者制造出来的后浪,掀起了 GitHub Trending ...

随机推荐

  1. Bootstrap 3 模态框播放视频

    Bootstrap 3 模态框播放视频 I'm trying to use the Modal feature from Bootstrap 3 to show my Youtube video. I ...

  2. 0427 scrum & 读后感

    5.Scrum团队成立 5.1 团队名称,团队目标.团队口号.团队照: 5.2 角色分配 6. 团队项目选题 详情见团队博客:http://www.cnblogs.com/alfredzhu/ htt ...

  3. 批处理——putCMAC小版本

    @ECHO off del telcc.vbs del telcc.bat del ftp1.txt del ftp1.bat @echo off setlocal enabledelayedexpa ...

  4. C++ 11 Template ... 与Decltype 测试

    #include <iostream> #include "string" using namespace std; template<typename T> ...

  5. linux命令:xargs

    1.命令介绍: xargs用来配合find命令查找的结果然后执行相应的命令 2.命令格式: find -type f -print | xargs file

  6. HTML5新增标签及具体用法

    html5自从推广普及以来,迅速被各大浏览器支持.采用html5设计的网页逐渐成为网页设计的时尚.下面就温习下html5的新增标签. HTML 5 中的新特性包括了嵌入音频.视频和图形的功能,客户端数 ...

  7. Xcode 的一些调式技巧

    XCode 内置GDB,我们可以在命令行中使用 GDB 命令来调试我们的程序.下面将介绍一些常用的命令以及调试技巧. po 命令:为 print object 的缩写,显示对象的文本描述(显示从对象的 ...

  8. 重装Ubuntu16.04及安装theano

    一.卸载现有的ubuntu系统 1.下载MbrFix,并放在C盘根目录. 2.管理员身份运行cmd命令符:切换到C盘根目录,然后输入命令符MbrFix /drive 0 fixmbr,回车确认即可. ...

  9. icmp_ping学习笔记

    1.用字符串指针做为发送缓冲区和接收缓冲区的指针: 2.icmp报文类型结构体可自行定义,也可用<netinet/ip_icmp.h>中定义好的strcut icmp结构体: 3.ip_h ...

  10. Zookeeper概论(对zookeeper的概论、原理、架构等的理解)

    Zookeeper概论(对zookeeper的概论.原理.架构等的理解) 一.概论 Zookeeper是一个分布式的.开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是h ...