Skip to content

Commit bf63109

Browse files
author
Jaeger
authored
Phase6 (#129)
* init phase 6 * 第 6 期:沉浸式适配个人总结 by 谢三弟 (#117) * finish * 添加注释 * update * 第 6 期:Android 单元测试-Mock及Mockito by JaonThink (#118) * begin phase5 * add phase_6 * Update summary.md * Delete summary.md * 修改错别字等 * Update jasonthink_2016110_android_unit-test-mock.md * Update jasonthink_2016110_android_unit-test-mock.md * Update jasonthink_2016110_android_unit-test-mock.md * 第 6 期:Linux 下用 Clion 编写及调用共享库的实践 by zetaoyang (#119) * update * delete * update * fixed it * 第 6 期:面向对象六大原则和设计模式 by melo (#121) * 第 6 期:面向对象六大原则和设计模式 by melo * 第 6 期:面向对象六大原则和设计模式 by melo * 修改错别字及格式错误 * finish jaeger's selectabletexthlper post (#122) * 第 6 期:RxJava 常用操作符使用及源码学习笔记 by yongyu (#124) * 第 4 期:Android View 的工作原理下 View 的 layout 和 draw 过程详解 * 添加审阅作者 * 修改写作日期 * begin phase5 * 第 5 期:Android WebView 实现点击界面图片滑动浏览和保存图片功能 * fixing * 第 6 期:RxJava 常用操作符使用及源码学习笔记 by yongyu * update pic * 第 6 期:使用贝塞尔曲线实现“一键下班”功能 by hymane (#125) * c1 * phase6 50% * phase6 50% * phase6 push * phase6 push * phase6 push * phase6 end * end * update image url * phase6 atom scripts (#128) * 第 6 期:Python 描述符指北 by Manjusaka (#120) * 第3期:Python 生成器和协程那点事儿 * 第三期作业内容修改 * 第六期作业:描述符指北 * 第 6 期:Retrofit 2.0 的应用场景概述 by shadow (#127) * 提交文章 * fix 细节 * fix 细节
1 parent 4fdbb6b commit bf63109

11 files changed

+4178
-0
lines changed

phase_6/atom-helper.md

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
# Atom-Helper 小脚本
2+
3+
这几天在尝试用新的文本编辑器 Atom,上手第一件事就是装插件咯。Atom 和 Sublime 一样,社区提供了各种各样的插件,来提供酷炫的功能。 我在 Atom 网站上搜索很多插件然后看看介绍下载体验,配置了一个简单的 Python 开发环境。
4+
5+
这个时候就想写个小脚本玩玩了,大概功能就是根据关键字搜索 Atom 的插件或者主题,然后排序展示前 N 个项目,当然直接通过命令行改变参数就更好了。
6+
7+
Atom 插件地址:[Packages](https://atom.io/packages)
8+
9+
Atom 主题地址:[Themes](https://atom.io/themes)
10+
11+
# 分析问题
12+
13+
整个脚本流程:
14+
15+
1. 根据关键字发出第一个搜索请求
16+
2. 解析出页面中的插件数据
17+
3. 找到「下一页」的链接,请求下一页
18+
4. 循环 2-3,直到最后一页,即「下一页」的链接不存在
19+
5. 对获取到的插件进行排序,然后输出 topN
20+
21+
先手动搜索一个关键字,用 Chrome 开发者工具查看一下请求内容:
22+
23+
![Search](http://ww4.sinaimg.cn/large/006y8mN6gw1f9v6ufxhuij30m80csdgm.jpg)
24+
25+
这就是一个普通的 GET 请求,直接把关键字作为参数添加在 url 后面就好了。
26+
27+
接下来查看一下页面的 HTML 元素:
28+
29+
![HTML](http://ww3.sinaimg.cn/large/006y8mN6gw1f9vct78948j30go0f8gmt.jpg)
30+
31+
可以根据 `class="grid-cell"` 这一属性,把每一个插件作为整体提取出来,然后再详细地解析出具体的字段。
32+
33+
然后找一下 「下一页」的链接:
34+
35+
![Next](http://ww3.sinaimg.cn/large/006y8mN6gw1f9vcuu27iuj30m801s74c.jpg)
36+
37+
![Next empty](http://ww3.sinaimg.cn/large/006y8mN6gw1f9vcvvflcaj30m801kdfs.jpg)
38+
39+
这也验证了前面的想法,最后一页的时候,「下一页」的链接不存在,这样就能正确地结束循环请求了。
40+
41+
最后就是获取一下 topN 的数据,然后输出出来了。
42+
43+
# 获取数据
44+
45+
首先根据前面分析的结果,先写一个大概的类结构,有几个方法需要实现。
46+
47+
```python
48+
class AtomHelper(object):
49+
def __init__(self, args):
50+
pass
51+
52+
def extract_item(self, cell):
53+
"""Extract item from cells."""
54+
pass
55+
56+
def run(self):
57+
"""Do search!"""
58+
pass
59+
60+
def topN(self, N=10):
61+
"""Get topN items."""
62+
pass
63+
```
64+
65+
## 解析字段
66+
67+
这个步骤直接使用 lxml 的 xpath 进行解析就好了,熟悉 xpath 的话很简单,最后把字段组装成一个字典。
68+
69+
```python
70+
def extract_item(self, cell):
71+
"""Extract item from cells."""
72+
73+
# Extract title, url, desc, download ...
74+
# ...
75+
76+
star = self.first(cell.xpath('.//a[@class="social-count"]/text()'))
77+
78+
return {
79+
'title': title,
80+
'url': url,
81+
'desc': desc,
82+
'download': self.number_format(download),
83+
'star': self.number_format(star),
84+
}
85+
```
86+
87+
需要注意的是,`xpath` 方法返回的通常都是 `list` 对象,通常都只需要第一项。所以这里写了一个方法来从序列中提取第一个元素,提供默认值避免每次判空。
88+
89+
```python
90+
def first(seq, default=''):
91+
"""Extract first item in sequence, or default value."""
92+
return seq[0] if seq else default
93+
```
94+
95+
另外网站上的数字是格式化后的,即 `2,333` 的形式,需要转换成正常的数字。
96+
97+
```python
98+
def number_format(num_str):
99+
"""Convert number string like '2,333' to integer."""
100+
return int(num_str.replace(',', '').strip()) if num_str else num_str
101+
```
102+
103+
## 循环请求页面
104+
105+
这一部分就是一个循环,不断地请求「下一页」的地址,同时解析页面中的数据和下一个链接。
106+
107+
```python
108+
def run(self):
109+
"""Do search!"""
110+
111+
next_url = self.search_url
112+
while next_url:
113+
html = etree.HTML(requests.get(next_url).text)
114+
115+
# Extract cells, next_url
116+
# ...
117+
118+
self.items.extend(map(self.extract_item, cells))
119+
```
120+
121+
## 获取 topN
122+
123+
这个用一下内置函数 `sorted` 就好了。
124+
125+
```python
126+
def topN(self, N=10, by='download'):
127+
return sorted(self.items, key=lambda item: item[by], reverse=True)[:N]
128+
```
129+
130+
然后把具体实现的代码组装起来,添加一些必要的输出提示,主体功能就完成了。
131+
132+
# 添加命令行参数
133+
134+
脚本可以搜索「插件」或者「主题」,这个需要枚举参数来控制;输出多少个结果,也是需要一个参数;是按下载量排序还是星星数量排序,还需要一个枚举参数。
135+
136+
> `argparse` 模块使得编写用户友好的命令行接口非常容易。程序只需定义好它要求的参数,然后 `argparse` 将负责如何从 `sys.argv` 中解析出这些参数。`argparse` 模块还会自动生成帮助和使用信息并且当用户赋给程序非法的参数时产生错误信息。
137+
138+
`argparse` 这个库简单易用,文档写得很详细,还有各种示例。[戳这里](http://python.usyiyi.cn/python_278/library/argparse.html)
139+
140+
```python
141+
def process_arg():
142+
parser = argparse.ArgumentParser(
143+
description='Atom helper to search packages or themes.',
144+
epilog='Have fun :)')
145+
146+
parser.add_argument('keyword',
147+
nargs='+', help='The keyword to search.')
148+
149+
parser.add_argument('-n', type=int, default=5,
150+
help='Show top N items.(default 5)')
151+
parser.add_argument('-t', '--type', choices=('packages', 'themes'),
152+
default='packages',
153+
help='The type to search.(default packages)')
154+
parser.add_argument('-s', '--sort', choices=('download', 'star'),
155+
default='download',
156+
help='Sort items by.(default download)')
157+
parser.add_argument('-v', '--verbose', action='store_true',
158+
help='Show debug messages.')
159+
160+
args = vars(parser.parse_args())
161+
args.update(keyword='+'.join(args['keyword']))
162+
return args
163+
```
164+
165+
这里主要的几个参数:
166+
167+
![args](http://ww1.sinaimg.cn/large/006y8mN6gw1f9vcnvzmjrj30go09z3zg.jpg)
168+
169+
- `-t` 指定搜索类型
170+
- `-s` 指定排序规则
171+
- `-n` 指定输出多少个数据
172+
- `-v` 显示 debug 信息
173+
- `-g` 显示帮助信息
174+
175+
# 最终结果
176+
177+
![final](http://ww1.sinaimg.cn/large/006y8mN6gw1f9vcmj4wt9j30m80bv0tv.jpg)
178+
179+
花时间写完这个脚本,发现并没有什么卵用😂,还不如自己去网站上搜搜呢。
180+
181+
不过第一次写命令行参数,感觉还是挺好玩的,熟悉了下 `argparse` 模块,以后用 Python 写小工具更方便了。
182+
183+
完整脚本地址: [atom_helper.py](https://github.com/brucezz/SomeScripts/blob/master/atom_helper.py), 欢迎拍砖评论。
184+
185+
# Reference
186+
187+
- [argparse 官网文档](http://python.usyiyi.cn/python_278/library/argparse.html)
188+
- [argparse - 命令行选项与参数解析(译)](http://blog.xiayf.cn/2013/03/30/argparse/)

0 commit comments

Comments
 (0)