Python爬虫实战之:快代理搭建IP代理池(Scrapy进阶版)
目录
前言
你好,我是Dr.叶子,用心写最优美的博客,弹最好听的钢琴!
项目背景
- 之前写了篇简版的作为入门,链接: Python爬虫实战之:快代理搭建IP代理池(简版)。为了进一步提升自己的能力,整理了这篇Scrapy进阶版。
- 网上的一些知识太过于零散,项目代码不规范,所以亲自搭建实战项目,与大家分享!
项目简介
本项目主要基于Scrapy框架搭建爬虫,爬取快代理网站的IP数据,经过有效性验证后,储存到PostgreSQL,形成IP代理池,方便以后开展更多的爬虫项目,能防止个人IP被封锁。
前期准备
- 操作系统:Windows 8.1;
- 语言版本:Python 3.7;
- 开发工具:Pycharm 2.1 专业版;
- 数据库:PostgresSQL 最新版;
- Scrapy框架:1.5.1 版本;
- requests 库:发起请求;
- faker 库:自动随机生成 user-agent;
- 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代理池(简版).
后语
- 原创内容,转载说明出处哦!
- 以上内容本人整理,亲测可行,如有任何问题,敬请指正,谢谢~~
- 点赞、收藏、也欢迎打赏,我弹钢琴你听呀~~哈哈!
更多推荐




所有评论(0)