Python写的deviantArt小爬虫

本文介绍了一个Python编写的deviantArt爬虫,用于构建推荐系统。爬虫采用多线程设计,包括扩展个人主页、抓取画廊、评分、数据库维护等线程,并具备中断恢复功能,结果存储在SQLite3数据库中。同时,针对并发限制问题,通过代理进行解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

功能简介

个人兴趣,打算爬个数据库拿来做推荐系统,几经周折终于写出了能稳定运行的D站爬虫。

这个爬虫抓取的内容包括作品名、作者名、上传日期、分类、评分和每个评分者,比较适合于简单的协同推荐算法。

为了效率,采用多线程方式,各线程间利用任务队列同步,结果使用sqlite3模块保存在两个数据库文件中。我的电脑不能24小时运行着它,所以加入了中断恢复的功能。

程序用到的模块如下:

import urllib2
import sqlite3
import re
import string
from bs4 import BeautifulSoup
import time
import Queue
import threading

程序流程



各线程功能

1.扩充个人主页线程Extender

这个线程包含了两个功能:抓取首页作者列表,放入个人主页队列中;以及获得现有队列中每一位作者的好友列表,放回到个人主页队列中。
启动后,它会等待中断恢复线程的报告,若没有待完成任务,就开始抓取首页并进行扩充,直到处理的个人主页达到指定量为止。若有待完成任务,则根据控制信号,直接退出(不扩充)或对中断恢复线程提供的队列进行扩充。
#--------------------------------------------------------------------#
#扩充个人主页列表的线程,输入为已备份主页队列qSaved,输出为已扩充主页队列qExtended和待处理主页队列qProfile
class ExtendProfileThread(threading.Thread):
    #初始化
    def __init__(self, thread_name, qsaved, qextended, qprofile, repeat=1000):
        threading.Thread.__init__(self, name = thread_name)
        self.input = qsaved
        self.output = qextended
        self.recycle = qprofile
        self.indexUrl = ''
        self.remaincount = repeat
        self.isRunning = 0
    
    #读取首页(仅在有首页地址传入时执行)
    def readIndex(self):
        url_legal_pattern = re.compile('http://.*') #用于检查url合法性的正则表达式模式
        strip_tag_pattern = re.compile('</?\w+[^>]*>') #用于去除html标签的正则表达式模式

        #检查URL合法性
        match = url_legal_pattern.search(indexUrl)
        if not match:
            print 'Illegal Index URL:', indexUrl
            return -1
        #读取页面
        try:
            site = urllib2.urlopen(indexUrl, timeout=10)
            content = site.read()
        except:
            print 'Time out for index:', indexUrl
            return -1
        #分析HTML结构
        soup = BeautifulSoup(content, 'html5lib')
        pagetitle = re.sub(strip_tag_pattern, '', soup.html.head.title.__str__())
        print '>>Index Page: '+pagetitle+'<<'
        #提取title和url信息
        articles = soup.findAll('a', attrs = {'href':True, 'title':True, 'class':'thumb'})    
        for item in articles:
            url = item['href']
            self.recycle.put(url[:url.find('art/')])
            self.remaincount = self.remaincount - 1
        site.close()
        return 0
    
    #扩充qProfile
    def extendProfile(self):
        #满足给定扩展次数则线程结束
        if self.remaincount <= 0:
            print 'Extend repeat finish'
            isRunning = 0
            return -1
        try:
            url = self.input.get(block = False)
        except Queue.Empty:
            return 1  #用于记录qSaved为空重试的次数
        self.output.put(url) #将取出的url直接放入qExtended
        time.sleep(0.5) #未必需要,防止访问太频繁被服务器打回
        retry = 6  #超时重试5次+初始1次
        while retry:
            try:
                page = urllib2.urlopen(url, timeout=10)
                content = page.read()
                page.close()
            except:
                if retry > 1:
                    print 'Time out for watchers:', url
                    print 'Retry', 7-retry
                retry = retry - 1
                if not retry:
                    print 'Cannot connect to url:', url
                    return 0
                continue
            retry = 0
        print 'Searching for watchers:', url
        #分析页面,使用html5lib,因为lxml有额外空格的bug,严重影响分析
        soup = BeautifulSoup(content, 'html5lib')
        #去掉包含id="groups-list-xxxx"的div字段,因为这是group列表而非watcher列表
        groupsdiv = soup.findAll('div', attrs = {'id' : re.compile('groups-list-.*')})
        for item in groupsdiv:
            item.extract()
        #提取watcher列表
        watchers1 = soup.findAll('a', attrs = {'class' : 'u', 'href' : True})
        watchers2 = soup.findAll('a', attrs = {'target' : '_self', 'href' : True})
        watchers = watchers1 + watchers2
        for watcher in watchers:
            if not watcher['href'] == url:
                try:
                    self.recycle.put(watcher['href'], timeout = 30) #将watcher的url放入待处理队列等待备份
                except Queue.Full:
                    print 'qProfile Full for 30s'
                self.remaincount = self.remaincount - 1
        return 0

    #线程运行
    def run(self):
        self.isRunning = 1
        global threadStatus
        threadStatus += 10000
        ret = 0
        retry = 0
        print 'Extender waiting...\n'
        while not self.indexUrl:
            continue        #等待主线程传入启动指令
        if not self.indexUrl == '/recover/':
            self.readIndex()
        while self.isRunning:
   
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值