聚沙成塔--爬虫系列(十)(这一刻你觉得找个程序员男朋友真幸福)

版权声明:本文为作者原创文章,可以随意转载,但必须在明确位置标明出处!!!

最近知乎上有一篇文章《月入五万的西二旗人教你如何活得像月薪五千》说的是北京西二旗程序员月入五万却过着月薪五千一样的生活,相比较其它行业,程序员的工资相对来说算是比较高的,程序员给人的形象永远都是眼镜、没刮过的胡须、乱糟糟的头发、还有个必备的电脑包(毕竟这可是吃饭的家伙),就是这种形象的人拿着月薪五万却过着月薪五千的生活,不过这是有原因的,996的工作时间(朝九晚九+周六),公司发的工装和冲锋衣可以一年四季穿到头,全年都不带买衣服的,唯一的爱好可能就是打打游戏,买买电子产品了,显示器一定要大,而且不止一个。所以如果你是妹子,那么找个程序员也是不错的,安全、放心。当然本章的重点不是讲怎么月入五万,本章的重点是讲怎么将数据写入到Excel表格中,方便我们做简单的统计。

上一章讲到了将如何将数据写入到文本文件里,但文本文件操作比较简单,如果我们要对数据做一些统计之类的操作那么文本文件就提供不了这些支持了,所以本章我们将数据写到Excel表格里做一些简单的统计。

openpyxl库

读写Excel需要用到第三方库,这里我们选择openpyxl库,这个库可以操作新版本的Excel,xlrt、xlwt两个库是用来读写老版本的Execl的,也就是扩展名是*.xls

安装openpyxl库

在ubuntu命令行终端输入以下命令就可以安装openpydl库了,如果没有报错则证明你安装好了。

sudo pip install openpyxl

校验openpyxl是否安装

在终端执行python--》回车--》import openpyxl--》回车。如果没有抛出异常证明该库已经安装好了。

workbook、worksheet

要读写Excel我们首先要弄清楚两个概念,

  • workbook: 工作簿,它的意思是我们打开一个Excel文档后,整个Excel文档被称作为一个工作簿。
  • worksheet: 工作表,如果我们新键一个Excel文件,然后打开它,我们可以看到底部有一个Sheet的选项,也就是我们的当前表格,也被称之为活动表格。

所以我们操作Excel表格的时候一定是要先有workbook后才能去操作worksheet,这点概念大家要弄清楚。

在内存中操作Excel

有了上面工作簿和工作表的概念,那么我们在内存中的操作步骤也要遵循上面的先有工作簿,再有工作表的规定。所以我们第一步是先创建一个工作簿,其次是获取一个活动的工作表,最后才是去操作工作表中的元素,代码如下

from openpyxl import Workbook
#创建一个工作簿
wb = Workbook()
#活取一个活动的工作表
ws = wb.active
#给A1单元赋值
ws['A1'] = 'test'
#保存Excel
wb.save('sample.xlsx')

执行结果

执行结果

从结果可以看到A1单元确实被赋值为test了。Sheet及时我们的活动表格。
如果你想修改Sheet的名称,或者想创建其它Sheet,下面这么做就可以了。

from openpyxl import Workbook
#创建一个工作簿
wb = Workbook()
#活取一个活动的工作表
ws = wb.active
#给A1单元赋值
ws['A1'] = 'test'

ws.title = 'test'

ws1 = wb.create_sheet("test1")
ws2 = wb.create_sheet("test2")

#保存Excel
wb.save('sample.xlsx')

执行结果

执行结果

从结果中可以看到我们将Sheet该成了test,并且新创建了test1,test2表格页签。

访问元素

这里需要注意的是,当我们在内存中创建一个worksheet的时候,它是不包含cells的,也就是不包含创建了单元格,只有当我们第一次去访问它的时候它才回去创建单元格,就像上面的代码ws['A1'] = 'test',当给A1单元格赋值的时候才回去创建单元格A1,这样做的目的相信大家都已经看出来了,节约资源,提高效率。cells操作还可以通过行、列来操作,如ws.cell(row=4, column=2, value=10),就是给第4行第二列赋值为10,各位可是执行运行试试,看看结果是不是这样的。

多行多列范围访问

  • 切片式范围访问: 切片是python里经常用到的操作,openpyxl库对范围访问也可以切片,语法如下
from openpyxl import Workbook
#创建一个工作簿
wb = Workbook()
#活取一个活动的工作表
ws = wb.active
cell_range = ws['A1' : 'C4']
print(cell_range)

访问A1到C4范围的所有cell,它的输出结果如下:

((<Cell 'Sheet'.A1>, <Cell 'Sheet'.B1>, <Cell 'Sheet'.C1>), 
(<Cell 'Sheet'.A2>, <Cell 'Sheet'.B2>, <Cell 'Sheet'.C2>),
 (<Cell 'Sheet'.A3>, <Cell 'Sheet'.B3>, <Cell 'Sheet'.C3>), 
(<Cell 'Sheet'.A4>, <Cell 'Sheet'.B4>, <Cell 'Sheet'.C4>))

结果返回的是一个元组,元组里每一项又包含了一个元组,该元组表示一行,一共4行。

  • 按列访问: 按列访问的语法如下
from openpyxl import Workbook
#创建一个工作簿
wb = Workbook()
#活取一个活动的工作表
ws = wb.active
col_range = ws['C:D']
print(col_range)

访问第C列到第D列的cell,执行结果如下

((<Cell 'Sheet'.C1>,), (<Cell 'Sheet'.D1>,))

从这里我们可以看出在内存中创建一个工作表时,只有访问cell的时候cell才回被创建,当我们的程序做出一下改变是,它的结果会是什么呢。

from openpyxl import Workbook
#创建一个工作簿
wb = Workbook()
#活取一个活动的工作表
ws = wb.active
ws['A4'] = 'SFS'
col_range = ws['C:D']
print(col_range)

执行程序后的结果变成了:

((<Cell 'Sheet'.C1>, <Cell 'Sheet'.C2>, <Cell 'Sheet'.C3>, <Cell 'Sheet'.C4>), 
(<Cell 'Sheet'.D1>, <Cell 'Sheet'.D2>, <Cell 'Sheet'.D3>, <Cell 'Sheet'.D4>))
  • 按行访问:按行访问跟按列访问是一样的,具体的执行结果是怎么杨的,大家可以自己去尝试一下,语法如下:
row5= ws[5]
row_range = ws[5:10]

迭代器访问行列区域

除了切片方式,openpyxl也提供了迭代器的方式来访问行列,如下按行优先访问2 * 3列范围的元素

from openpyxl import Workbook
#创建一个工作簿
wb = Workbook()
#活取一个活动的工作表
ws = wb.active
for row in ws.iter_rows(min_row=1, max_col=3, max_row=2):
    for cell in row:
        print(cell)

执行结果如下:

<Cell 'Sheet'.A1>
<Cell 'Sheet'.B1>
<Cell 'Sheet'.C1>
<Cell 'Sheet'.A2>
<Cell 'Sheet'.B2>
<Cell 'Sheet'.C2>

按列优先访问如下:

from openpyxl import Workbook
#创建一个工作簿
wb = Workbook()
#活取一个活动的工作表
ws = wb.active
for row in ws.iter_cols(min_row=1, max_col=3, max_row=2):
    for cell in row:
        print(cell)

执行结果如下:

<Cell 'Sheet'.A1>
<Cell 'Sheet'.A2>
<Cell 'Sheet'.B1>
<Cell 'Sheet'.B2>
<Cell 'Sheet'.C1>
<Cell 'Sheet'.C2>

如果你需要迭代所有的行列那你可以像这样做:

from openpyxl import Workbook
#创建一个工作簿
wb = Workbook()
#活取一个活动的工作表
ws = wb.active

#按行优先迭代
ws['C9'] = 'test'
print(tuple(ws.rows))

#按列优先迭代
print(tuple(ws.columns))

执行结果到底怎样,各位自行探索一下。

加载一个已存在的Excel文档

加载一个已存在的Excel文档非常简单,加载后返回一个workbook对象,那么我们就可以像上面介绍的操作去处理加载后的文档了。

from openpyxl import load_workbook
wb2 = load_workbook('sample.xlsx')
print wb2.get_sheet_names()

图表

图表有2D和3D图表,下面以一个2D的图表作为例子,这个例子是openpyxl手册上的例子

from openpyxl import Workbook
from openpyxl.chart import (
    AreaChart,
    Reference,
    Series,
)

wb = Workbook()
ws = wb.active

rows = [
    ['Number', 'Batch 1', 'Batch 2'],
    [2, 40, 30],
    [3, 40, 25],
    [4, 50, 30],
    [5, 30, 10],
    [6, 25, 5],
    [7, 50, 10],
]

for row in rows:
    ws.append(row)

chart = AreaChart()
chart.title = "Area Chart"
chart.style = 13
chart.x_axis.title = 'Test'
chart.y_axis.title = 'Percentage'

cats = Reference(ws, min_col=1, min_row=1, max_row=7)
data = Reference(ws, min_col=2, min_row=1, max_col=3, max_row=7)
chart.add_data(data, titles_from_data=True)
chart.set_categories(cats)

ws.add_chart(chart, "A10")

wb.save("area.xlsx")

执行后的结果:

图表结果

openpyxl提供了非常丰富的图表支持,像条形图、柱状图、饼图、曲线图等等,有兴趣的同学可以到https://openpyxl.readthedocs.io/en/default/查看。当然想合并单元格、公式、高亮、文本格式等等用法都可以到上面提供的网址查看。

用图表来表示哪个用户获得的好笑数最高

下面我们把爬到的数据写入到Excel表格中,并且用图表来先生哪个用户的好笑数最高,代码如下,源码放在https://github.com/Gavinxyj/Python/tree/master/python_study/Scrapy/modules欢迎大家fork、Stars

# Scheduler.py
from urllib import request
from urllib import error
import re
import os
from Excel import ExcelOper


class Scheduler(object):

    def __init__(self, url, user_agent):
        self.url = url
        self.headers = {'User-Agent': user_agent}
        self.excel_obj = ExcelOper()

    def read_html(self, codec):
        '''[read_html]
        
        [读取html页面内容]
        
        Arguments:
            url {[string]} -- [url地址]
            headers {[dict]} -- [用户代理,这里是一个字典类型]
            codec {[string]} -- [编码方式]
        
        Returns:
            [string] -- [页面内容]
        '''
        # 构建一个请求对象
        try:
            req = request.Request(self.url, headers=self.headers)
            # 打开一个请求
            response = request.urlopen(req)
            # 读取服务器返回的页面数据内容
            content = response.read().decode(codec)

            return content

        except error.URLError as e:
            print(e.reason)
            return None       
        
    def match_element(self, content, pattern):
        '''[match_element]
        
        [匹配元素]
        
        Arguments:
            content {[string]} -- [文本内容]
            pattern {[object]} -- [匹配模式]

        Returns:
            [list] -- [匹配到的元素]
        '''
        # 匹配所有用户信息
        
        userinfos = re.findall(pattern, content)
        
        return userinfos
    def write_file(self, content):
        with open('./qiubai.txt', 'a+') as fp:
            fp.write(content + '\n')

    def get_content(self):
        content = self.read_html('utf-8')
        pattern = re.compile(r'<div class="article block untagged mb15[\s\S]*?class="stats-vote".*?</div>', re.S)
        if content:
            userinfos = self.match_element(content, pattern)
            infos = []
            if userinfos:
                pattern = re.compile(r'<a href="(.*?)".*?<h2>(.*?)</h2>.*?<div class="content">(.*?)</div>.*?<i class="number">(.*?)</i>', re.S)
                picture = re.compile(r'<div class="thumb">.*?src="(.*?)"', re.S)
                for userinfo in userinfos:
                    item = self.match_element(userinfo, pattern)
                    pictures = self.match_element(userinfo, picture)
                    try:
                        if item:
                            userid, name, content, num = item[0]
                            # 去掉换行符,<span></span>,<br/>符号
                            userid = re.sub(r'\n|<span>|</span>|<br/>', '', userid)
                            name = re.sub(r'\n|<span>|</span>|<br/>', '', name)
                            content = re.sub(r'\n|<span>|</span>|<br/>|\x01', '', content)
                            
                            if pictures:
                                path = './users/'
                                if not os.path.exists(path):
                                    os.makedirs(path)

                                request.urlretrieve('http:' + pictures[0], path + os.path.basename(pictures[0]))
                                infos.append((userid, name, int(num), content, pictures[0]))
                                #print((userid, name, num, content, pictures[0]))
                                #self.write_file(userid + '\t' + name + '\t' + content + '\t' + num + '\t' + pictures[0])

                            else:
                                #print((userid, name, content, num))
                                infos.append((userid, name, int(num), content))
                                #self.write_file(userid + '\t' + name + '\t' + content + '\t' + num)
                    except Exception as e:
                        print(e)
                self.excel_obj.write_excel(infos)

if __name__ == '__main__':
  url = 'https://www.qiushibaike.com'
  user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'
  handle = Scheduler(url, user_agent)
  handle.get_content()
# Excel.py
from openpyxl import Workbook
from openpyxl.chart import (
    AreaChart,
    Reference,
    Series,
)

class ExcelOper(object):

    def __init__(self):
        self.wb = Workbook()

    def write_excel(self, infos):
        try:
            ws = self.wb.active
            # 增加表头
            ws.append(['id', 'username', 'funny_num', 'context', 'url'])
            for row_index,row_value in enumerate(infos, 2):
                
                for col_index, col_value in enumerate(row_value, 1):
                    ws.cell(row=row_index, column=col_index, value=col_value)

            self.draw_chart(ws)
            self.wb.save('qiubai.xlsx')
        except Exception as e:
            raise e

    def draw_chart(self, ws):
        chart = AreaChart()
        chart.title = "Joker Chart"
        chart.style = 13
        chart.x_axis.title = 'User'
        chart.y_axis.title = 'Funny Num'

        cats = Reference(ws, min_col=2, min_row=1, max_row=25)
        data = Reference(ws, min_col=3, min_row=1, max_col=3, max_row=25)
        chart.add_data(data, titles_from_data=True)
        chart.set_categories(cats)

        ws.add_chart(chart, "A30")
    def read_excel(self):
        pass

整个程序增加了一个Excel.py文件,主要用来主要用来操作excel表格,里面使用到的方法在本章都已经讲过,更多的图表制作有兴趣的同学可以自己去研究研究,最后奉上执行结果。

执行结果

PS: 如果你是“表”姐,需要经常在网上收集内容,那么找个程序员男朋友吧,他会帮你弄得妥妥的,再也不用加班,不用熬夜了,哈哈哈...


欢迎关注我:「爱做饭的老谢」,老谢一直在努力...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,924评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,902评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,716评论 0 239
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,783评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,166评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,510评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,784评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,476评论 0 196
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,196评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,459评论 2 243
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,978评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,321评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,964评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,046评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,803评论 0 193
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,530评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,420评论 2 265

推荐阅读更多精彩内容

  • 本例为设置密码窗口 (1) If Application.InputBox(“请输入密码:”) = 1234 Th...
    浮浮尘尘阅读 13,248评论 1 20
  • 使用首先需要了解他的工作原理 1.POI结构与常用类 (1)创建Workbook和Sheet (2)创建单元格 (...
    长城ol阅读 8,258评论 2 25
  • 1.1 VBA是什么 直到90年代早期,使应用程序自动化还是充满挑战性的领域.对每个需要自动化的应用程序,人们不得...
    浮浮尘尘阅读 21,490评论 6 49
  • 转自链接 2.3.5 IF函数 2.3.6 CountIf和SumIf函数 2.3.7 Lookup函数 2.3....
    腿毛裤阅读 12,535评论 0 0
  • 打卡
    安心的心灵花园阅读 182评论 0 0