从零开始写一只爬虫 · 虫子爬爬

从零开始写一只爬虫 · 虫子爬爬

经过前面的步骤,一个小巧的虫子就出炉了。下面是它的全部身子骨:

import csv
import http
import socket
import threading
import urllib
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, thread=5, timeout=2, ):
        self.thread = thread
        # 队列
        self.queue = []
        # 已经爬行过的集合
        self.history = []
        # 爬行数据记录
        self.dataSaver = dataSaver
        # 失败 Log 记录
        self.failSaver = failSaver
        # 线程们
        self.THREADS = []
        # 互斥锁
        self.queueLock = threading.Lock()
        # 带 cookie 的请求器
        self.requester = self.login()
        # 设置超时
        socket.setdefaulttimeout(timeout)

    def login(self):
        auth_url = 'http://freedns.afraid.org/zc.php?from=L2RvbWFpbi8='
        # 登录用户名和密码
        loginInfo = {
            'username': 'jinyu121@126.com',
            'password': '413431390',
            'remember': 1,
            'from': 'L2RvbWFpbi8=',
            'action': 'auth'
        }
        # urllib 进行编码
        login_data = urllib.parse.urlencode(loginInfo).encode()
        headers = {
            "Host": "freedns.afraid.org",
            "Connection": "keep-alive",
            "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36",
            "Accept": "*/*",
            "Accept-Encoding": "gzip, deflate, sdch",
            "DNT": "1",
            "Accept-Language": "zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4",
            "Referer": "http://freedns.afraid.org/domain/?ls=1",
        }
        # 初始化一个 CookieJar 来处理 Cookie
        cookieJar = http.cookiejar.CookieJar()
        # 实例化一个全局 opener
        opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cookieJar))
        # 获取 cookie
        req = urllib.request.Request(auth_url, login_data, headers)
        result = opener.open(req)
        print('登录成功')
        return opener

    def addUrlBlock(self, start=100, end=200):
        for index in range(start, end):
            url = 'http://freedns.afraid.org/subdomain/edit.php?edit_domain_id=' + str(index)
            self.addUrl(url)

    def addUrl(self, url):
        # 得到锁
        self.queueLock.acquire()
        if not url in self.history:
            # 添加 URL
            self.queue.append(url)
        # 释放锁
        self.queueLock.release()

    def getUrl(self):
        while self.queue:
            url = None
            # 得到锁
            self.queueLock.acquire()
            # 获得 URL
            if self.queue:
                url = self.queue.pop(0)
                self.history.append(url)
            # 释放锁
            self.queueLock.release()
            yield url
        raise StopIteration

    def findInfo(self, nowUrl, data):
        status = data.find('option', value=True).text.strip()
        self.dataSaver.write(status.split(' '))

    def findUrl(self, nowUrl, data):
        aSet = data.find_all('a')
        for a in aSet:
            self.addUrl(a.href)

    def runThread(self):
        for url in self.getUrl():
            try:
                # 得到想要的信息
                data = BeautifulSoup(self.requester.open(url).read())
                # 处理信息
                self.findInfo(url, data)
                print("Success %s" % url)
                # 发现链接
                # self.findUrl(url, data)

            except:
                print("Fail %s" % url)
                self.failSaver.write(["Fail", url])

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

        self.THREADS = []
        # 生成线程
        for i in range(self.thread):
            self.THREADS.append(threading.Thread(target=self.runThread))
        # 开启线程
        for i in range(self.thread):
            self.THREADS[i].start()
        # 等待线程完成
        for i in range(self.thread):
            self.THREADS[i].join()

        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, thread=3)
    spider.addUrlBlock(300, 310)
    spider.run()
    print('全部完成')

诚然,这个爬虫不能与 Apache Nutch 或者 WebMagic 相比,并且功能什么的也都很弱,没有进度监控、没有自动调度,各种简陋。不过,作为练习数据结构、练习 Python 语法、临时有点什么体力活需要自动解决、满足自己好奇心的时候,它还是很有效的。

下面是一些爬取成果:

抓取结果
抓取结果

另外还可以把它改造成为一个图片抓取器什么的。谁会改写代码的话可以帮帮忙。

当然,有些神人还能通过抓取 QQ 空间啊什么的获得了 TX 的 3000W 好友关系数据。唔……大神请收下我中了一箭的膝盖。

好,本次教程就到这里,第一次写连载教程也就到这里。如果有什么建议的话,请联系我。

留下评论