最近一直在爬新闻,对于新闻爬取的套路还是比较熟悉的。一个群友发布了一个爬文章入word的任务,我果断接单,自我挑战一下,更何况完成任务还有赏金,哈哈。
任务大概是这样的,有一个文章列表【http://www.shui5.cn/article/Bulletin/】,大约7W的量,每篇文章保存到一个word文件中。
任务比较清晰,观察一下文章内容,发现所有的文章格式都是一致的,这样就比较简单了,只要能爬出一篇文章就搞定了。
但是,现在有一个问题需要优先解决,就是如何用python操作word文档。
第一步:何用python操作word文档
首先明白一点,手动创建的以“.doc”或“.docx”结尾的文件并不是真正的word文件,以word文件的方式打开时会报错的。
网上给的方法是使用python-docx扩展包。
pip install python-docx
使用方法:
1 #引入docx扩展包
2 import docx
4 #创建document 对象
5 document = docx.Document()
7 #添加标题,第二个参数是标题的等级
8 document.add_heading(title, 1)
10 #添加段落
11 document.add_paragraph(content)
13 #保存成一个docx文件
14 document.save(file_name)
我的需求是往word文档里添加标题和正文,没有图片,也不需要调整格式,所以有这几个接口就够用了。如果想查看更多接口,可以去这里
https://python-docx.readthedocs.io/en/latest/index.html
第二步:爬一篇文章试试
首先分析一下页面结构,页面包含顶部导航、中间的文章列表和底部的分页。
顶部导航是爬虫入口,从这12个分类开始,进入每个分类的文章列表
中部是文章列表,底部是分页,先爬当前页的文章列表,然后从底部进入下一页的文章列表,直到没有下一页的时候。
也可以爬完第一页的时候,分析后续页面的链接特征,同时爬剩下的所有页。但是这样会造成爬虫并发过大,导致一些不可预知的问题。
我采用的是第一种,每次爬一页,然后从“下一页”的链接进入下一个文章列表,直到结束。
先把最后的代码贴出来,再逐一说明
1 #!/usr/bin/env python
2 # -*- encoding: utf-8 -*-
3 # Created on 2018-04-18 09:42:32
4 # Project: 1
6 from pyspider.libs.base_handler import *
7 import re, docx
9 URL_ARR = {
10 'http://www.shui5.cn/article/FaGuiJieDu/'
11 }
13 class Handler(BaseHandler):
14 crawl_config = {
15 }
17 @every(minutes=24 * 60)
18 def on_start(self):
20 #爬虫入口
21 for each in URL_ARR:
22 self.crawl(each, callback=self.news_list)
24 #新闻列表
25 @config(age=60 * 60)
26 def news_list(self, response):
27 #当页新闻列表
28 if response.doc('.arcList').html():
29 for each in response.doc('.arcList a').items():
30 if re.match("http://www.shui5.cn/article/.", each.attr.href, re.U):
31 self.crawl(each.attr.href, callback=self.news_content)
33 #下一页
34 next_page_index = response.doc('.pagelist.clearfix a').length - 2
35 if u"下一页" in response.doc('.pagelist.clearfix a').eq(next_page_index).text():
36 url = response.doc('.pagelist.clearfix a').eq(total_pages).attr.href
37 self.crawl(url, callback=self.news_list)
39 #新闻内容
40 @config(priority=2)
41 @catch_status_code_error
42 def news_content(self, response):
44 if response.doc('.arcContent').html():
45 document = docx.Document()
46 title = response.doc('.articleTitle h1').text().replace('/', u'、')
47 document.add_heading(title, 1)
48 for a in response.doc('.articleDes, .arcContent table p').items():
49 document.add_paragraph(a.text())
50 file_name = (u'/pyspider/htmlfiles/wordFile/1/' + title + '.docx').encode('gbk')
51 document.save(file_name)
52 else:
53 return {}
第46行代码
title = response.doc('.articleTitle h1').text().replace('/', u'、')
这是取文章标题的逻辑,最后有个replace的操作,是因为有的文章标题中有“/”,document.save的时候,会把这个斜杠解析成一级目录,导致保存乱套,所以把标题中的斜杠都替换成顿号了。unicode编码表中没有中文顿号的符号,所以前面加u。
还有50行代码
file_name = (u'/pyspider/htmlfiles/wordFile/1/' + title + '.docx').encode('gbk')
这句是拼word文件名称和保存路径的操作,最后有个encode操作,即编码成gbk,此处是有特殊含义的
我们都知道,在python中默认的编码格式是unicode,而unicode能编码的字符比较少,遇到汉字就无能为力了,只能绕道走,通过把汉字编码成别的编码格式,然后储存第三方编码后的结果。具体的这种第三方编码选谁呢,我在代码第二行已经定义了,即utf-8。所以最后保存的word文件的名称和内容都是utf-8编码的。我的linux的默认编码格式是utf-8(你可以echo $LANG查看一下,或者通过修改/etc/profile文件来修改),所以在linux下是没啥问题的。关键是传输到windows下就出问题了。windows系统默认编码格式是gbk,而word内容的编码是utf-8,所以最后就看到word文件名称乱码而内容正常显示。这也就是我为什么在拼出word文档的标题后,要将它转码为gbk的原因了。