身为运维岗的小屌丝, 对于shell, 甚至文本三剑客, 总是不可分割的, 因为工作上, 特别是日志处理查找等, 更能体现出三剑客的强大功力. 虽然说教程网上都有, 但是缺乏练习总会让自己过眼就忘, 刚好Cu上的shell板块有很多练习, 所以写了个专门抓这些问题的爬虫, 来方便我们做练习. 事不宜迟, 简单讲解下分析步骤
为了达到练习效果, 咱们不用bs或者pyquery, 所以我们在内容整理方面用re库就够了
过程分解
CU shell板块链接: http://bbs.chinaunix.net/foru... 打开链接如图所示, 可以看到红框选中的, 就是shell板块的问题, 咱们的任务就是要将这些帖子的内容, 要爬出来.
第一步: 定位问题帖子
要想找到准确定位这些帖子, 咱们要先找到这些帖子的html标签的共同点 以及和 别的便签的不同点, 右键查看源码, 随便搜索一个中文用来定位相应地方的源码:
如图所示, 我通过其中一个问题关键字: "grep -v l两个可否简化", 已经定位到相应地方的源码, 可以看到问题描述,链接都包含在一个<th></th>标签内, 那咱们直接过滤出每个th块就好了
第二步: 从问题帖子块提取有用信息
咱们已经确定了, 需要的内容在th块中, 那么一个块有什么信息是咱们关注的呢?
从图可以看出, 一个短短的th块, 就已经包含了: 问题类型, 问题链接 和问题的标题了, 问题类型和问题标题将后期输出整理时会用到, 而问题链接是我们马上需要用到的, 因为我们需要从里面获取具体内容,获取的方法也是很简单 可以明显看到, 虽然地址在a标签的href属性中, 但是a标签却有2个, 所以如果单纯获取a标签, 那么将会有多余的结果, 所以这里先定位到 "]" 再来获取
base_content_pattern = re.compile(
'\[<a[^>]+>(?#quesiton_type)([^<]+)[^"]+"(?#quesiton_url)([^"]+).*?>(?#quesiton_title)([^<]+)</a'
)
正则讲解:
(?#..)只是用来注释的, 并不会对匹配结果有干扰; 所以 (?#content)xxx 是表示 xxx是关于content的正则
关于问题类型的正则:
(?#quesiton_type)([^<]+): 用于捕获: >文本处理<a位置的 '文本处理'
关于问题链接的正则:
(?#quesiton_url)([^"]+): 用于捕获: <a href="thread-4259680-1-1.html 位置的 '非"内容,也就是链接地址'
关于问题标题的正则:
(?#quesiton_title)([^<]+)</a: 用于捕获: >grep -v l两个可否简化</a> 位置的 'grep -v l两个可否简化'
经过上诉的处理, 我们已经从一个th块, 取出 问题类型, 问题链接 和问题的标题了, 那么我们将这些结果存到一个列表, 后期在根据这列表内容来做具体的问题内容获取和整理输出
第三步: 对具体的 问题链接 做具体内容的抓取
和第一步同样的做法, 查看页面源代码, 搜索问题关键字, 找到结果如下:
而且很幸运的可以看到, 所处的那块有个唯一的字符: JIATHIS_CODE_HTML4, 所以咱们可以通过这个位置, 来截获到下面的 </td>, 也就是:
content_pattern = re.compile('JIATHIS_CODE_HTML4">(.*?)</td', re.S)
其实到了这一步, 爬虫已经完成80%了, 接下来就是最最后的清洗整理
第四部步: 对 具体的问题内容 作数据整理
在我们匹配出问题内容时, 数据还是很粗糙, 因为有很多html标签, 例如 , 等等, 就好像上个步骤提取的内容:
u' \r\n<div class="t_fsz">\r\n<table cellspacing="0" cellpadding="0"><tr><td class="t_f" id="postmessage_24624780">\r\n\u73b0\u6025\u9700\u89e3\u51b3\u6b64\u95ee\u9898\uff1a<br />\r\n2\u4e2a\u6587\u4ef6\uff1a<br />\r\n1.txt 5\u5217 1.5G<br />\r\n2.txt 12\u5217 2.6G<br />\r\n awk \'NR==FNR{a[$1" "$2" "$3]=$4" "$5}NR>FNR{if(a[$3" "$6" "$10]!=""&&a[$3" "$6" "$10]!=" ")print $1,$2,$3,a[$3" "$6" "$10]}\' 1.txt 2.txt<br />\r\n\u8fd0\u884c\u540e\u62a5\u9519\uff1athere is not enough availiable memory<br />\r\n\u8bf7\u95ee\u8be5\u5982\u4f55\u89e3\u51b3\uff1f'
可以看到有很多 ,"等等, 所以我们需要该转换的转换, 该去掉的去掉,咱们构建一个转换字典
self.html_flag_converation = {
' ': ' ',
'<br />': '\n',
'"': '"',
'<': '<',
'>': '>'
}
意思很明确, 就是将对应的html标签转换成它在页面表示的符号
除了页面的html, 有些提问者可能会无意中多敲了好多回车, 导致咱们过滤的结果也是有好多换行, 但咱们并不需要那么多行, 所以统一换成一个
content_result = re.sub(r'<.*?>|\r\n|\r', '', content_result)
content_result = re.sub(r'\n+', '\n', content_result)
经过上面的处理, 文本已经算是可以了~ 我们再把之前获取的 问题类型 问题标题 加上去, 就是一个完整的问题了
在输出方面, 咱们有个点需要考虑, 就是保存到文件, 有时候咱们需要马上来一发练习题, 联系完就算了;但是有时候, 有些勤奋的同学, 想要通过计划任务去定时抓, 然后保存;
所以为了兼容这两个, 用一个比较简单的方法实现:
class Spider:
"""
China Unix Bss spider
"""
def __init__(self, output=None):
...
if output:
self.result_output = open(output, 'w')
else:
self.result_output = sys.stdout
if __name__ == '__main__':
file_name = None
if len(sys.argv) == 2:
file_name = sys.argv[1]
spider = Spider(output=file_name)
spider.start()
[root@iZ23pynfq19Z ~]# python spider_cu.py # 直接输出到屏幕
[root@iZ23pynfq19Z ~]# python spider_cu.py file # 直接输出到文件, 屏幕只是打印写入的问题id
好了, 通过上面的输出方式, 再通过如下将问题标题, 问题类型等等整合到一起, 爬虫就完成了:
self.result_output.write('-' * 30 + '\n')
self.result_output.write('Question_title: %s\nQuestion_type: %s\nQuestion_url: %s\n' % (
content_title.encode('utf8'),
content_type.encode('utf8'),
content_url.encode('utf8')
))
self.result_output.write('Question_content:\n%s' % content_result.encode('utf8'))
爬虫源码地址