Scrapy爬虫工程设计

最近做了一些爬虫的工作,并涉及到了工程的部署和自动化,借此机会整理一下,工程结构如图:

 

image.png

工程主要包含4个部分:

  • 获取有效代理ip
  • 数据管理
  • 不同任务的spider
  • spider在线调度和管理

1.获取有效代理ip

代理IP可从国内的几个网站爬取,如西刺。可以肯定免费的代理IP大部分都是不能用的,不然别人为什么还提供付费的(不过事实是很多代理商的付费IP也不稳定,也有很多是不能用)。所以采集回来的代理IP不能直接使用,需要写一个过滤程序去用这些代理访问目标网站,看是否可以正常使用。我的项目里所有爬虫任务都是每日定时开启的,所以爬取和过滤的过程被设计在了一个spider中,主任务开启的前三个小时执行 ,因为检测代理是个很慢的过程。

以西刺网站为例,创建任务spider:

 

class Proxy(Spider):
    name = "scrapy_proxy"
    #设置custom_settings,执行爬取代理ip的任务时不使用代理 
    custom_settings = {
        'DOWNLOADER_MIDDLEWARES' : {
            'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware': 543,
        }
    }
    base_url = "http://www.xicidaili.com/nn/"


    def __init__(self, *a , **kw):
        super(Proxy, self).__init__(*a, **kw)
        self.sql = SqlHelper()#数据管理方法
        self.create_proxyurl_table()#创建存储有效代理ip的table

    def create_proxyurl_table(self):
        command = (
            "CREATE TABLE IF NOT EXISTS {} ("
            "`id` INT(8) NOT NULL AUTO_INCREMENT,"
            "`url` TEXT(20) NOT NULL ,"
            "`create_time` DATETIME NOT NULL,"
            "PRIMARY KEY(id)"
            ") ENGINE=InnoDB".format(config.proxy_url_table)
        )
        self.sql.create_table(command)

爬取前10页的IP:

 

    def start_requests(self):
        for i in range(1,10):
            url = self.base_url + str(i)
            yield Request(
                url = url,
                headers = {
                        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                        'Accept-Encoding': 'gzip, deflate, sdch',
                        'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6',
                        'Connection': 'keep-alive',
                        'Host': 'www.xicidaili.com',
                        'Upgrade-Insecure-Requests': '1',
                        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:51.0) Gecko/20100101 Firefox/51.0',
                },
                callback = self.parse_all,
                errback = self.error_parse
            )

解析内容,验证IP有效性,并存储

 

    def parse_all(self,response):
        proxys = response.xpath("//table[@id='ip_list']/tr").extract()
        for i ,proxy in enumerate(proxys):
            if i==0 :continue
            sel = Selector(text = proxy)
            ip = sel.xpath("//td[2]/text()").extract_first()
            port = sel.xpath("//td[3]/text()").extract_first()
            speed = sel.xpath("//td[7]/div[@class='bar']/@title").extract_first().replace('秒','')
            connect = sel.xpath("//td[8]/div[@class='bar']/@title").extract_first().replace('秒','')

            proxy = str(ip) + ':'+str(port)
            self.validateIP(proxy)


    def validateIP(self,proxy):
        try:
            requests.post('目标网站地址',proxies={"http":proxy},timeout=10)
        except:
            util.log(proxy + '  connect failed')
        else:
            util.log("grab ip :%s" % (proxy))
            dt = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            msg = (None,proxy,dt)
            command = ("INSERT IGNORE INTO {}"
                    "(id,url,create_time)"
                    "VALUES(%s,%s,%s)".format(config.proxy_url_table)
            )
            self.sql.insert_data(command, msg)

2.数据存储

创建一个工具类,包含常用的建表,查询,插值等操作

 

#-*- coding: utf-8 -*-

import logging
import mysql.connector
import utils
import config

from singleton import Singleton


class SqlHelper(Singleton):
    def __init__(self):
        self.database_name = config.development_database_name
        self.init()

    def init(self):
        self.database = mysql.connector.connect(**config.development_database_config)
        self.cursor = self.database.cursor()

        self.create_database()
        self.database.database = self.database_name

    def create_database(self):
        try:
            command = 'CREATE DATABASE IF NOT EXISTS %s DEFAULT CHARACTER SET \'utf8\' ' % self.database_name
            utils.log('sql helper create_database command:%s' % command)
            self.cursor.execute(command)
        except Exception, e:
            utils.log('SqlHelper create_database exception:%s' % str(e), logging.WARNING)

    def create_table(self, command):
        try:
            utils.log('sql helper create_table command:%s' % command)
            self.cursor.execute(command)
            self.database.commit()
        except Exception, e:
            utils.log('sql helper create_table exception:%s' % str(e), logging.WARNING)

    def insert_data(self, command, data):
        try:
            #utils.log('insert_data command:%s, data:%s' % (command, data))

            self.cursor.execute(command, data)
            self.database.commit()
        except Exception, e:
            utils.log('sql helper insert_data exception msg:%s' % str(e), logging.WARNING)

    def execute(self, command):
        try:
            utils.log('sql helper execute command:%s' % command)
            data = self.cursor.execute(command)
            self.database.commit()
            return data
        except Exception, e:
            utils.log('sql helper execute exception msg:%s' % str(e))
            return None

    def query(self, command):
        try:
            #utils.log('sql helper execute command:%s' % command)

            self.cursor.execute(command)
            data = self.cursor.fetchall()

            return data
        except Exception, e:
            utils.log('sql helper execute exception msg:%s' % str(e))
            return None

    def query_one(self, command):
        try:
            utils.log('sql helper execute command:%s' % command)

            self.cursor.execute(command)
            data = self.cursor.fetchone()

            return data
        except Exception, e:
            utils.log('sql helper execute exception msg:%s' % str(e))
            return None

3. Spiders

关于Scrapy相关的基础知识,请查阅Python系列文章

4.Spider在线调度和管理

SpiderKeeper是一个scrapy的管理后台,基于Scrapyd和Flask。界面如下:

 

image.png

 

主要功能包括:

  • Job Dashboard
  • Periodic Jobs
  • Deploy
  • Running Stats
  • Manage



作者:SamDing
链接:https://www.jianshu.com/p/b360f53930e7
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值