聚沙成塔--爬虫系列(九)(落地生根)

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

上一章我们讲了类的概念,专业术语叫OOP(面向对象的编程),同时也讲了类的三大属性,「封装」、「继承」、「多态」,并且也用代码实现了继承和多态,让初学者更能直观的去理解继承和多态究竟是怎么回事的。相信认真阅读过的同学对类已经有了一个初步的认识。那么本章将会把上一篇文章的代码使用面向对象的编程去改写它。但这不是本章的重点,本章的重点是把我们的数据保存到磁盘上,专业术语叫「持久化」

持久化

将数据写到磁盘文件,也就是普通的文本文件是本章的主题,也是最简单的持久化方案,但是简单文件的存储不便于我们查找和统计,所以后面的章节我们将会讲到把数据存储到Excel、数据库中。

内建函数open()

open内建函数返回一个文件对象,如果文件对象不存在,则抛出一个OSError异常。文件对象不仅可以用来访问普通的磁盘文件,它也可以访问其它抽象层面的文件,像网络文件,你也可以使用open打开一个url来读取页面的元素。

open函数的定义

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

  • file: file是一个类似路径的对象,给出要打开的文件的路径名,可以是绝对路径也可以是相对于当前工作目录的路径
  • mode: 模式,就是以什么样的方式去打开文件。
  • buffering:缓冲是用于设置缓冲策略的可选整数。 通过0以切换缓冲(仅允许在二进制模式下),1选择行缓冲(仅在文本模式下可用),整数> 1表示固定大小的块缓冲区的大小(以字节为单位),该参数大于零的意思是对文件对象的读写操作为当前设置的值
  • encodeing: 编码,意思是以什么样的编码方式去读写文件,你可以设置该参数为‘utf-8’、‘gbk’等编码方式
  • errors: error参数是一个可选字符串,指定如何处理编码和解码错误,但是在二进制模式模式下无效
  • newline: 通用换行符控制,它的作用是用来帮助我们访问不同平台的文件,不同平台用来表示结束符号是不同的,如\r,\n,或者\r\n
  • closefd: 如果closefd为False,并且提供了文件描述符而不是文件名,则当文件关闭时,底层文件描述符将保持打开状态。 如果给定文件名,则closefd必须为True(默认值),否则会引发错误。
  • opener: 自定义打开器。

文件的打开模式

符号 说明
'r' open for reading (default)
'w' open for writing, truncating the file first
'x' open for exclusive creation, failing if the file already exists
'a' open for writing, appending to the end of the file if it exists
'b' binary mode
't' text mode (default)
'+' open a disk file for updating (reading and writing)
'U' universal newlines mode (deprecated)

模式之间都可以通过‘+’符号来组合,‘r+w’打开一个可读又可写的文件对象,open默认打开模式为可读。

从文件读

open打开一个已存在的文件后,我们需要从文件里读取文件内容,读取文件内容有以下几个函数

  • read()方法用来直接读取字节到字符串中, 最多读取给定数目个字节. 如果没有给定 size参数(默认值为 -1)或者 size 值为负, 文件将被读取直至末尾
  • readline()每次读取文件中的一行
  • readlines()将文件中的每一行都读取到一个list列表中。

写入文件

写入文件同样用的的文件对象依然是open内建函数返回的,写入文件提供了以下函数

  • write()和read,readline()函数相反,它把含有文本数据或二进制数据块的字符串写入到文件中去.
  • writelines()将一个列表的数据写入打开的文件中,参数一定要是一个list类型的

文件内移动

seek()可以在文件中移动文件指针到不同位置,如果你学过C语言那么就跟C语言的fseek函数是一样的,0默认位置表示从文件开头算起,1当前位置表示从当前位置算起,2表示从为文件末尾算起。

写入数据到磁盘文件

try:
    fp = open('./test.txt', 'w')
    for index in range(100):
        fp.write('hello world' + '\n')
    fp.close()
except Exception as e:
    raise e

在当前路径下打开一个test.txt文件,如果当前路径下没有这个文件那么它将会在当前目录下创建这个文件。fp是我们打开文件返回的文件句柄对象,这个是系统资源,拿到这个文件对象了就可以对它进行相应的操作,操作完后一定要记得释放对象,因为这个对象是系统资源。w是操作文件的模式,如果我们想要每次在文件的末尾追加新写入的数据可以用模式‘a+’,执行程序后你将会在程序的当前目录下看到一个test.txt文件,打开文件可以看到文件写入了100行‘hello world’

从磁盘中读取文件

try:
    fp = open('./test.txt', 'r')
    for content in fp.readlines():
        print(content)
    fp.close()
except Exception as e:
    raise e

如果以‘r’模式打开文件,若test.txt文件不存在于当前目录,那么程序将会抛出一个FileNotFoundError异常,执行程序可以后终端上会打印出100行‘hello world’。

with语句

with语句是个大神器啊, with语句的然而,with 语句的目的在于从流程图中把 try,except 和finally 关键字和资源分配释放相关代码统统去掉, 而不是像 try-except-finally 那样仅仅简化代码使之易用,不过with语句只支持上下文管理协议的对象。这显然意味着只有内建了"上下文管理"的对象可以和with 一起工作,上下文管理协议本章不做详细介绍,看到这里的同学如果有兴趣可以自己在网上了解了解。既然with语句这么神奇了,那么我们的读写文件就可以写成如下形式。

#读文件
with open('./test.txt') as fp:
    for content in fp.readlines():
        print(content)

#写文件
with open('./test.txt', 'w') as fp:
    fp.write('hello world' + '\n')

try-except语句没有了,释放资源的close函数也没有了,这些都被with语句做了,是不是再也不用担心忘记释放资源了。本章要讲的内容就到此介绍了,最后奉上改写后的源码

from urllib import request
from urllib import error
import re
import os


class Scheduler(object):

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

    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)

            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/>', '', 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]))
                             
                                print((userid, name, content, num, pictures[0]))
                                self.write_file(userid + '\t' + name + '\t' + content + '\t' + num + '\t' + pictures[0])
                            else:
                                print((userid, name, content, num ))
                                self.write_file(userid + '\t' + name + '\t' + content + '\t' + num)
                    except Exception as e:
                        print(e)

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()                                   

执行结果

执行结果

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

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

推荐阅读更多精彩内容