前言

你好,我是Dr.叶子,用心写最优美的博客,弹最好听的钢琴!

项目背景

项目简介

本项目主要基于Scrapy框架搭建爬虫,爬取快代理网站的IP数据,经过有效性验证后,储存到PostgreSQL,形成IP代理池,方便以后开展更多的爬虫项目,能防止个人IP被封锁。

前期准备

  1. 操作系统:Windows 8.1;
  2. 语言版本:Python 3.7;
  3. 开发工具:Pycharm 2.1 专业版;
  4. 数据库:PostgresSQL 最新版;
  5. Scrapy框架:1.5.1 版本;
  6. requests 库:发起请求;
  7. faker 库:自动随机生成 user-agent;
  8. psycopg2 库:连接PostgresSQL 用;

 

讲解1:项目搭建

1. Scrapy的安装: 打开 Pycharm 的 Terminal 命令终端执行

# 方法1:直接安装
pip install scrapy

# 方法2:若连接超时,则切换国内镜像
pip install scrapy -i http://pypi.douban.com/simple --trusted-host pypi.douban.com

Terminal 命令终端随便一个目录下都行,只要你的 Python 环境配置了系统环境变量。最好还是cd到你要存放项目的文件夹下吧,方便后面操作!

 
2. 新建一个工程: 打开 Pycharm 的 Terminal 命令终端

# cd 进入到你要存放该工程的目录,然后执行下面命令
scrapy startproject proxy

在这里插入图片描述

proxy: 工程名称;

 
3. 构建爬虫spider: cd 进入该工程目录下执行

scrapy genspider kuaidaili www.kuaidaili.com

在这里插入图片描述

kuaidaili: 爬虫名称;
www.kuaidaili.com: 爬虫的域名(按代码写的为准,图片是旧的截图);
一个工程中可以存在多个spider爬虫, 但是名字必须唯一

 
4. 进入工程: Pycharm打开项目,进入到proxy工程

在这里插入图片描述

 
5.查看工程结构

proxy
├── proxy
│   ├── __init__.py
│   ├── items.py        # Items的定义,定义抓取的数据结构
│   ├── middlewares.py  # 定义Spider和DownLoader的Middlewares中间件实现。 
│   ├── pipelines.py    # 它定义Item Pipeline的实现,即定义数据管道
│   ├── settings.py     # 它定义项目的全局配置
│   └── spiders         # 其中包含一个个Spider的实现,每个Spider都有一个文件
│       ├── __init__.py
│       └── kuaidaili.py  # 爬虫文件
└── scrapy.cfg    #Scrapy部署时的配置文件,定义了配置文件路径、部署相关信息等内容

在这里插入图片描述

到这里,一个Scrapy爬虫项目的基础架构初现,接下来就是完成业务逻辑,实现爬数据、存数据的功能。


 

讲解2:理解Scrapy框架

1. 数据流图: 来自官网
在这里插入图片描述

具体处理步骤,请参阅: Scrapy 官方文档

 
2. Scrapy 组件介绍:

  • Scrapy Engine(Scrapy引擎)
    引擎负责控制所有组件之间的数据流 系统,并在发生某些操作时触发事件(框架核心)。

  • Scheduler(调度程序)
    接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回。

  • Downloader(下载器)
    下载网页内容, 并将网页内容返回给爬虫程序

  • Spiders(爬虫)
    爬虫是用户编写的自定义类,用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。可以从中提取出链接,让Scrapy继续抓取下一个页面。

  • Item 项目
    定义爬取结果的数据结构,爬取的数据会赋值成改Item对象。

  • Pipeline(项目管道)
    负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。

  • Downloader Middlewares(下载器中间件)
    位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。

  • Spider Middlewares(爬虫中间件)
    介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。

  • Scheduler Middewares(调度中间件)
    介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。


 

讲解3:Python连接PostgresSQL

1. 安装 psycopg2 库: 连接postgres数据库必须的库

# 用豆瓣等国内镜像加速下载
pip install psycopg2 -i http://pypi.douban.com/simple --trusted-host pypi.douban.com

 
2. 初步使用:

# 步骤1:定义数据库连接参数
database = "proxy"
user = "postgres"
password = "123456"
host = "localhost"
port = "5432"

# 步骤2:连接数据库
db = psycopg2.connect(database=database, user=user,
                           password=password, host=host, port=port)

# 步骤3:开启游标
cur = self.db.cursor()

# 步骤4:利用游标执行数据库的“增、删、查、改”
... ...

# 步骤5:最后记得关闭数据库的连接和游标
db.close()
cur.close()

在项目中的具体使用,请看后面讲解和代码~


 

讲解4:创建IP代理池数据库

-- 建表
CREATE TABLE if not exists proxy
(
	id SERIAL PRIMARY KEY,
	ip varchar(15),
	port varchar(6),
	degree varchar(10),
	type varchar(10),
	position varchar(128),
	operator varchar(6),
	speed float(53),
	last_time timestamp
);

-- 表注释
COMMENT ON TABLE proxy IS '代理池表';
-- 字段注释
COMMENT ON COLUMN proxy.id IS 'id自增主键';
COMMENT ON COLUMN proxy.ip IS 'ip地址';
COMMENT ON COLUMN proxy.port IS '端口';
COMMENT ON COLUMN proxy.degree IS '匿名度';
COMMENT ON COLUMN proxy.type IS '类型:HTTP/HTTPS';
COMMENT ON COLUMN proxy.position IS '位置';
COMMENT ON COLUMN proxy.operator IS '运营商:电信、联调、移动等';
COMMENT ON COLUMN proxy.speed IS '响应速度';
COMMENT ON COLUMN proxy.last_time IS '最后验证时间';

创建完成,如图:

在这里插入图片描述

这里对于 PostgresSQL 的可视化管理,建议使用 Navicat Premium 15 版本(或更高版本)的客户端,不然会无法创建表。


 

讲解5:编写代码逻辑

1. items.py: 定义数据结构

class KuaidailiItem(scrapy.Item):
    """
    kuaidaili爬虫的Item(自定义)
    """
    ip = scrapy.Field()  # ip
    port = scrapy.Field()  # port
    degree = scrapy.Field()  # 匿名度
    proxy_type = scrapy.Field()  # 类型
    position = scrapy.Field()  # 位置
    operator = scrapy.Field()  # 运营商
    speed = scrapy.Field()  # 响应速度
    last_time = scrapy.Field()  # 最后验证时间

这里根据快代理爬取的字段来定义,也就是最终存储在本地的字段。

 
2. kuaidaili.py: 爬虫程序,抓取页面数据

# 记得导入对应Item类
from proxy.items import KuaidailiItem


class KuaidailiSpider(scrapy.Spider):
    name = 'kuaidaili'      # 本爬虫名称
    allowed_domains = ['www.kuaidaili.com']     # 域名
    start_urls = ['https://www.kuaidaili.com/free/inha/1']     # 爬虫初始页,首先爬的是这一页

    p = 1   # 页码计数器
    pages = 2   # 请求总页数

    def parse(self, response):
        """
        接收请求start_urls链接成功后的response结果,筛选后传递到管道文件
        :param response: 请求返回的结果
        """
        # 1. 首先爬去第一页,也就是start_urls地址
        list_tr = response.selector.xpath("//div[@id='list']/table/tbody/tr")

        # 1.1 循环取数据
        for list_td in list_tr:
            # 1.1.1 创建Item
            item = KuaidailiItem()
            # 1.1.2 筛选数据
            item['ip'] = list_td.xpath("./td[@data-title='IP']/text()").extract_first()
            item['port'] = list_td.xpath("./td[@data-title='PORT']/text()").extract_first()
            item['degree'] = list_td.xpath("./td[@data-title='匿名度']/text()").extract_first()
            item['proxy_type'] = list_td.xpath("./td[@data-title='类型']/text()").extract_first()
            # 1.1.3 由于'位置position'与'运营商operator'字段是一个单元格的,所以这里做了下拆分
            position_operator = list_td.xpath("./td[@data-title='位置']/text()").extract_first()
            item['position'] = position_operator.rsplit(' ', 1)[0]
            item['operator'] = position_operator.rsplit(' ')[-1]
            # 1.1.4 处理“秒”字
            speed = list_td.xpath("./td[@data-title='响应速度']/text()").extract_first()
            item['speed'] = speed.rsplit('秒', 1)[0]
            item['last_time'] = list_td.xpath("./td[@data-title='最后验证时间']/text()").extract_first()
            # 1.1.5 传递到管道
            yield item

        # 2.循环改变路径,发起请求
        self.p += 1
        if self.p <= self.pages:
            next_url = 'https://www.kuaidaili.com/free/inha/' + str(self.p)
            url = response.urljoin(next_url)  # 构建绝对url地址
            yield scrapy.Request(url=url, callback=self.parse)  # 交给调度去继续爬取下一页信息

自定义 pages 值,定义爬取的总页数,后面循环改变URL请求数据。

 
3. pipelines.py: 管道文件,解析爬虫传来的页面数据

  • 第一种写法:保存数据到 json 文件;
class KuaidailiJsonPipeline(object):
    """
    保存为json文件的管道类(自定义)
    """

    def __init__(self):
        self.f = open("kuaidaili_pipeline.json", "wb")

    def process_item(self, item, spider):
        content = json.dumps(dict(item), ensure_ascii=False) + ", \n"
        self.f.write(content.encode("utf-8"))
        return item

    def close_spider(self, spider):
        self.f.close()
  • 第二种写法:保存到数据库;
import psycopg2
from scrapy import log


class KuaidailiPostgresPipeline(object):
    """
    保存到postgres数据库的管道类(自定义)
    """
    def __init__(self, host, user, password, database, port):
        """
        初始化函数
        :param host: 主机ip
        :param user: 数据库用户名
        :param password: 数据库密码
        :param database: 所属数据库
        :param port: 端口号
        """
        self.host = host
        self.user = user
        self.password = password
        self.database = database
        self.port = port
       
        # 步骤1:连接数据库
        self.db = psycopg2.connect(database=self.database, user=self.user,
                                   password=self.password, host=self.host, port=self.port)
        self.cur = self.db.cursor()

    @classmethod
    def from_crawler(cls, crawler):
        """
        搜寻器对象类(scrapy默认)
        提供对所有Scrapy核心的访问,诸如设置和信号之类的组件;
        :param crawler: 搜寻器
        :return: 返回给上面的初始化函数中__init__
        """
        return cls(
            host=crawler.settings.get("PGSQL_HOST"),
            user=crawler.settings.get("PGSQL_USER"),
            password=crawler.settings.get("PGSQL_PASS"),
            database=crawler.settings.get("PGSQL_DATABASE"),
            port=crawler.settings.get("PGSQL_PORT"),
        )

    def open_spider(self, spider):
        """
        当spider被开启时调用(scrapy默认)
        :param spider: spider对象
        """
        # 步骤2:清空数据库表
        self.clear_db()

    def process_item(self, item, spider):
        """
        当处理item数据时调用(scrapy默认必须)
        每个item pipeline组件都需要调用该方法,这个方法必须返回一个 Item
        或任何继承类对象,或是抛出 DropItem 异常,被丢弃的item将不会被之后的pipeline组件所处理。
        :param item: 被爬取的item
        :param spider: spider对象
        :return: item
        """

        ip_port = item["ip"] + ":" + item["port"]
        headers = {'User-Agent': Factory.create(locale='zh-CN').user_agent()}

        # 步骤3:拼凑sql语句和参数
        data = (item["ip"], item["port"], item["degree"], item["proxy_type"],
                    item["position"], item["operator"], item["speed"], item["last_time"])
        sql = "insert into proxy.public.proxy(ip,port,degree,type,position,operator,speed,last_time) " \
              "values(%s,%s,%s,%s,%s,%s,%s,%s)"

        try:
            # 步骤4:存入数据库
            self.cur.execute(sql, data)  # 执行sql
            self.db.commit()  # 事务提交
        except Exception as e:
            self.db.rollback()  # 发生错误时回滚
            log.msg("存入数据库失败:" + str(e), level=log.ERROR)

        
        # 返回给引擎,告诉引擎这里item已经处理结束
        return item

    def close_spider(self, spider):
        """
        当spider被关闭时调用(scrapy默认)
        关闭数据库连接
        :param spider: spider对象
        """
        # 步骤5:关闭数据库连接
        self.db.close()
        self.cur.close()

    def clear_db(self):
        sql = "TRUNCATE TABLE proxy.public.proxy RESTART IDENTITY"
        try:
            self.cur.execute(sql)   # 执行sql
            self.db.commit()  # 事务提交
        except Exception as e:
            self.db.rollback()  # 发生错误时回滚
            log.msg("清空proxy数据库失败:" + str(e), level=log.ERROR)

无论选用哪种方法,必须将它的类添加到 settings.py 里的 ITEM_PIPELINES 配置,如下:

ITEM_PIPELINES = {
	# 二选一,或者都用
    'proxy.pipelines.KuaidailiJsonPipeline': 200,
    'proxy.pipelines.KuaidailiPostgresPipeline': 300,
}

两种方法二选一就行,当然也可以同时用到,需要在 settings.py 里配置 ITEM_PIPELINES 每个管道类的优先级数字。通常将这些数字定义在0-1000范围内整型值,数值越低,优先级越高,先运行。


 

讲解6:配置数据库信息

1. 在 settings.py 文件里配置:

# Postgres数据库配置(自定义)
PGSQL_HOST = 'localhost'
PGSQL_DATABASE = 'proxy'
PGSQL_USER = 'postgres'
PGSQL_PASS = '123456'
PGSQL_PORT = 5432

配置后,在pipelines.py的第二种写法里的搜寻器对象from_crawler会自动读取到。


 

讲解7:配置Scrapy日志log

1. 在 settings.py 文件里配置:

# 日志配置
LOG_ENABLED = True  # 默认: True,启用logging
LOG_ENCODING = 'utf-8'  # 默认: 'utf-8',logging使用的编码
LOG_FILE = "proxy.log"  # 默认: None,在当前目录里创建logging输出文件的文件名
LOG_LEVEL = 'INFO'  # 默认: 'DEBUG',log的最低级别
LOG_STDOUT = False  # 默认: False 如果为 True,进程所有的标准输出(及错误)将会被重定向到log中

日志等级:

  • CRITICAL - 严重错误(critical)
  • ERROR - 一般错误(regular errors)
  • WARNING - 警告信息(warning messages)
  • INFO - 一般信息(informational messages)
  • DEBUG - 调试信息(debugging messages)

注意:如果不配置日志,在接下来启动爬虫的时候,会将所有爬虫运行中的debug信息及抓取到的信息打印在运行窗口中。


 

讲解8:启动爬虫

打开 Terminal 进入 proxy 工程目录下: 如无意外,一打开就是该目录下

# 执行命令
scrapy crawl kuaidaili

运行指定名称的爬虫,这里是“kuaidaili”。


 

项目演示

1. Json 文件:

在这里插入图片描述

 
2. 数据库:

在这里插入图片描述

 
3. 最终生成: 多出2个文件,Log 和 Json 文件

在这里插入图片描述


项目代码

1. settings.py:

BOT_NAME = 'proxy'

SPIDER_MODULES = ['proxy.spiders']
NEWSPIDER_MODULE = 'proxy.spiders'

# Obey robots.txt rules
ROBOTSTXT_OBEY = False  # 是否遵守机器人规则

# Configure maximum concurrent requests performed by Scrapy (default: 16)
CONCURRENT_REQUESTS = 1  # 并发量

DOWNLOAD_DELAY = 3  # 下载延迟

# Override the default request headers:
DEFAULT_REQUEST_HEADERS = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en',
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0',
}

ITEM_PIPELINES = {
    # 'proxy.pipelines.ProxyPipeline': 100,
    'proxy.pipelines.KuaidailiJsonPipeline': 200,
    'proxy.pipelines.KuaidailiPostgresPipeline': 300,
}

# Postgres数据库配置(自定义)
PGSQL_HOST = 'localhost'
PGSQL_DATABASE = 'proxy'
PGSQL_USER = 'postgres'
PGSQL_PASS = '123456'
PGSQL_PORT = 5432

# 日志配置
LOG_ENABLED = True  # 默认: True,启用logging
LOG_ENCODING = 'utf-8'  # 默认: 'utf-8',logging使用的编码
LOG_FILE = "proxy.log"  # 默认: None,在当前目录里创建logging输出文件的文件名
LOG_LEVEL = 'INFO'  # 默认: 'DEBUG',log的最低级别
LOG_STDOUT = False  # 默认: False 如果为 True,进程所有的标准输出(及错误)将会被重定向到log中

完整代码,请看本人 GitHub.


GitHub地址

欢迎加星: https://github.com/chengyun427/proxy_scrapy.


参考来源

【1】主要参考: Scrapy官网.
【2】重要参考: Scrapy爬虫框架案例实战.
【3】本人博客: Python爬虫实战之:快代理搭建IP代理池(简版).


后语

  1. 原创内容,转载说明出处哦!
  2. 以上内容本人整理,亲测可行,如有任何问题,敬请指正,谢谢~~
  3. 点赞、收藏、也欢迎打赏,我弹钢琴你听呀~~哈哈!
Logo

加入社区!打开量化的大门,首批课程上线啦!

更多推荐