从零开始写一只爬虫 · 雁过留痕

从零开始写一只爬虫 · 雁过留痕

刚才我们都是把抓取结果输出到了控制台上,关闭窗口后,这些记录就没有了。

如何保存爬虫辛辛苦苦抓取来的结果呢?这时候,我想到了文件……

为啥不用数据库啊?因为连接 MySql 数据库太麻烦,还不如文件来的直接方便。数据库删除的时候还得写 delete * from XXX where 1,我懒……文件多好,直接删掉就 OK 了。

更关键的,数据库,怎么的不得再起动个服务啊?即使是 sqli 这样的轻量级数据库……你能直接拿记事本打开么?嘎嘎~~

封装文件读写

Python 给我们封装了文件的操作。我们可以像这样对其进行二次封装,更方便使用:

class Saver:
    def __init__(self, path='./', filename='result.txt', method='w+'):
        self.path = path
        self.filename = filename
        self.method = method
        # 打开的文件
        self.FILE = 'Unknown'

    def open(self):
        self.FILE = open(self.path + self.filename, self.method)

    def write(self, data):
        self.FILE.write(data)

    def close(self):
        self.FILE.close()

感觉这样封装意义不是太大哈……

直接输出为 CSV 文件

上面直接操作文件是很方便,但是!我想让获取到的东西直接做成一个 Excel,或者简单一点,一个 CSV 文件。这样就能用 Excel 很快地进行操作了(比如排序啊,做个统计图啊什么的)。

工具就是拿来为我们服务的嘛……

所以我们可以对 Python 的 excel 读写类和 CSV 读写类做类似下面这样的封装:

class CsvSaver:
    def __init__(self, path='./', filename='result.txt', method='w+'):
        self.path = path
        self.filename = filename
        self.method = method
        # 打开的文件
        self.FILE = None
        self.WRITER = None

    def open(self):
        self.FILE = open(self.path + self.filename, self.method)
        self.WRITER = csv.writer(self.FILE, quoting=csv.QUOTE_ALL)

    def write(self, data):
        '参数是一个 list'
        self.WRITER.writerow(data)
        self.FILE.flush()

    def close(self):
        self.FILE.close()

这样的话封装就好一点了。

注意,我的逻辑是,有一个数据要写,那么就立刻从缓冲区里面 flush 出来。虽然加大了 io,但是万一万一程序崩溃掉了,缓冲区里面地东西都能被及时保存出来啊~不会 “丢数据”。

为什么不直接输出成 xls 或者 xlsx 文件?因为我懒……没看懂官方 doc。就这样吧,够用就好。

代码变长了……

我的思路很奇怪:如果某个网页获取成功,那么我将获取到的信息输出到一个文件里面;如果这个网页获取失败,那么将失败的原因输出到另一个文件里面,以备以后心血来潮复查。

于是,我们的代码变成了这样:

import csv
import requests
from bs4 import BeautifulSoup


class CsvSaver:
    def __init__(self, path='./', filename='result.txt', method='w+'):
        self.path = path
        self.filename = filename
        self.method = method
        # 打开的文件
        self.FILE = None
        self.WRITER = None

    def open(self):
        self.FILE = open(self.path + self.filename, self.method)
        self.WRITER = csv.writer(self.FILE, quoting=csv.QUOTE_ALL)

    def write(self, data):
        '参数是一个 list'
        self.WRITER.writerow(data)
        self.FILE.flush()

    def close(self):
        self.FILE.close()


class Spider:
    def __init__(self, dataSaver, failSaver=None):
        self.queue = []
        self.dataSaver = dataSaver
        self.failSaver = failSaver

    def addUrlBlock(self, start=100, end=200):
        for index in range(start, end):
            url = 'http://www.lagou.com/gongsi/' + str(index) + '.html'
            self.addUrl(url)

    def addUrl(self, url):
        self.queue.append(url)

    def run(self):
        self.dataSaver.open()
        if self.failSaver:
            self.failSaver.open()

        while self.queue:
            url = self.queue.pop(0)
            try:
                response = requests.get(url, timeout=5, allow_redirects=False)
                data = BeautifulSoup(response.text)
                name = data.h1.a.text.strip()
                location = data.find('ul', 'info_list_with_icon').find(attrs={'class': 'location'}).span.text
                self.dataSaver.write([name, location])
                print("Success %s" % url)
            except:
                self.failSaver.write(["Fail", url])

        self.dataSaver.close()
        if self.failSaver:
            self.failSaver.close()


if __name__ == '__main__':
    dataStore = CsvSaver(filename='result.csv')
    logStore = CsvSaver(filename='log.csv')
    spider = Spider(dataSaver=dataStore, failSaver=logStore)
    spider.addUrlBlock(100, 200)
    spider.run()
    print('全部完成')

调理还算清晰吧?

小插曲

周日太累了,没有更新。昨天晚上,一个保存按下去,整个电脑卡掉了。卡回来发现,整个系统都只读了。关机关不住,强行断电、重启,直接进入 Ubuntu 的紧急模式了。

这不会修啊……重装吧。忘了当初 UEFI 启动的时候还顺带加载了 Legzcy 起动的相关东西,这次一直弄不好 UEFI 起动。好容易装上了,挂载/home 发现挂载不上,还是一直进入紧急模式。今天早上全盘备份,全盘格式化,重装到现在……然而到现在 Matlab 这个大家伙都还没有装。一直解压缩失败。

留下评论