pyspider爬一批文章保存到word中
最近一直在爬新闻,对于新闻爬取的套路还是比较熟悉的。一个群友发布了一个爬文章入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 3 4 #创建document 对象 5 document = docx.Document() 6 7 #添加标题,第二个参数是标题的等级 8 document.add_heading(title, 1) 9 10 #添加段落 11 document.add_paragraph(content) 12 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 5 6 from pyspider.libs.base_handler import * 7 import re, docx 8 9 URL_ARR = { 10 'http://www.shui5.cn/article/FaGuiJieDu/' 11 } 12 13 class Handler(BaseHandler): 14 crawl_config = { 15 } 16 17 @every(minutes=24 * 60) 18 def on_start(self): 19 20 #爬虫入口 21 for each in URL_ARR: 22 self.crawl(each, callback=self.news_list) 23 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) 32 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) 38 39 #新闻内容 40 @config(priority=2) 41 @catch_status_code_error 42 def news_content(self, response): 43 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的原因了。