前言

上篇文章吧啦吧啦讲了一些有的没的,现在还是回到主题写点技术相关的。本篇文章作为基础爬虫知识的最后一篇,将以爬虫程序的模块设计来完结。

在我漫(liang)长(nian)的爬虫开发生涯中,我通常将爬虫程序分为四大模块。

如图,除了代理模块是根据所需引入程序,请求、解析、储存模块是必不可少的。

代理模块

代理模块主要是构建代理IP池。在第三篇中讲过为什么需要代理IP,因为很多网站是通过请求频率来识别爬虫,即记录一个IP在一段时间内的请求次数,所以可以通过更换代理IP来提高爬取效率。

概念

什么是代理IP池?

和线程池、连接池的理念一样,预先将多个代理IP放入一个公共区域供多个爬虫使用,每次用完之后再放回。

为什么需要代理池?

正常情况下,我们在程序中是这样添加代理IP的。

proxies = {
'https': 'https://183.220.xxx.xx:80'
}
response = requests.get(url, proxies=proxies)

这样我们就只能使用一个IP,这时候可能有人就会说:

就算使用集合可以存放多个代理IP,但是如果IP失效需要删除,或者添加新的IP时,还是一样需要终止程序修改代码。我在初学编程的时候,老师就经常说这么一句话:

直到现在,这句话也时常在耳边萦绕。而代理模块就是提供了灵活增删代理IP、验证IP有效性的功能。

实现

目前,一般使用MySQL来存放代理IP。先看一下代理池的表设计。

CREATE TABLE `proxy` (
`ip` varchar(100) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

我的表结构设计比较简单粗暴,只有一个字段。在开发中可以根据自己的需要来进行细分。

再看一下表里面的数据:

从图中看着,代理IP是由支持的协议 + IP + port组成。

最后,代理池代码奉上:

import requests
import pymysql class proxyPool:
# 初始化数据连接
def __init__(self, host, db, user, password, port):
self.conn = pymysql.connect(host=host,
database=db,
user=user,
password=password,
port=port,
charset='utf8')
# 从数据库获取ip
def get_ip(self):
cursor = self.conn.cursor()
cursor.execute('select ip from proxy order by rand() limit 1')
ip = cursor.fetchone()
# 如果代理ip表中有数据,则进行判断,没有则返回空
if ip:
judge = self.judge_ip(ip[0])
# 如果ip可用直接将其放回,不可用再重新调用此方法从数据库获取一个IP
if judge:
return ip[0]
else:
self.get_ip()
else:
return '' # 判断ip是否可用
def judge_ip(self, ip):
http_url = 'https://www.baidu.com'
try:
proxy_dict = {
"http": ip,
}
response = requests.get(http_url, proxies=proxy_dict)
except Exception:
self.delete_ip(ip)
return False
else:
code = response.status_code
if code in (200, 299):
return True
else:
self.delete_ip(ip)
return False # 从数据库中删除无效的ip
def delete_ip(self, ip):
delete_sql = f"delete from proxy where ip='{ip}'"
cursor = self.conn.cursor()
cursor.execute(delete_sql)
self.conn.commit()

代理池工作流程主要分为两部分:

  1. 从数据中获取IP。如果数据库没有可用IP,则表示不使用代理,返回空;如果有IP,则进入下一步
  2. 对IP进行有效性验证。如果IP无效,删除IP并重复第一步;如果IP有效,则返回IP

使用

代理池最终的目的还是提供有效代理IP。玩的比较花的可以将代理池与爬虫程序分离,将代理池独立成一个web接口,通过url来获取代理IP,需要使用Flask或者Django来搭建一个web服务。

我一般就是直接放在爬虫程序中。样例代码如下:

pool = proxyPool('47.102.xxx.xxx', 'test', 'root', 'root', 3306)
proxy_ip = pool.get_ip()
url = 'https://v.qq.com/detail/m/m441e3rjq9kwpsc.html'
proxies = {
'http': proxy_ip
}
if proxy_ip:
response = requests.get(url, proxies=proxies)
else:
response = requests.get(url)
print(response.text)

代理IP的来源

这个之前也讲过,代理IP可以付费购买或者从网上使用免费的。因为免费IP存活率低,所以代理池主要是面向于免费IP。

一般都是单独开发一个爬虫程序来爬取免费的IP,并放入到数据库中,然后验证可用性。

请求/解析模块

在前几篇写的爬虫样例中,都是对单个url进行的爬取。而爬虫程序往往都是以网站为单位进行的爬取。归根结底,都是基于请求模块和解析模块来设计实现的。

如果想爬取整个网站,首先必须确定一个网站入口,即爬虫程序第一个访问的url。然后接着对返回的网页进行解析,获取数据或者获取下一层url继续请求。

这里就拿腾讯视频举个栗子,我们来**爬取动漫的信息*。

1. 选择网站入口

分析需求,选取网站入口。此时,需要明确的是:动漫频道url就是网站入口

我们对网站入口,即动漫频道进行请求后,解析返回的网页内容。我们从页面中可以发现,动漫频道下有国漫、日漫、战斗等分类。

查看网页源码:

如上图,我们可以从动漫首页解析出来各个分类的url

2.分类请求

在获取到各个分类的url之后,继续发起请求。这里首先对国漫的url进行请求,返回的网页内容如下:

如图,都是国漫分类下的动漫列表。在浏览器中,我们点击哪个动漫就能进入它的播放页,所以在这个页面上我们可以解析到这些国漫的播放页链接

我们查看此页面的网页源码:

如图,我们可以获取到各个国漫播放页的url

3.定向到信息页

以第一个国漫斗罗大陆为例,我们获取到它的播放页url,进行请求并返回播放页内容。

我们发现,点击右上角的斗罗大陆就会进入详情页。所以我们需要解析右上角详情页的url进行请求,来获取详情页的网页内容。

4.获取数据

对详情页的网页内容进行解析,得出自己想要的数据,具体代码在第一篇文章的样例中。

从上面的四个步骤来看,爬虫对网站的爬取就是层层递进,逐级访问。我们要找准网站入口,明确想要获取的数据内容,规划好网站入口到获取数据的路径

当然其中还是有很多可以优化的地方,例如从第二步可以略过第三步,直接请求第四步的详情页。我们比对一下播放页和详情页的url。

# 斗罗大陆的播放页和详情页
https://v.qq.com/x/cover/m441e3rjq9kwpsc.html
https://v.qq.com/detail/m/m441e3rjq9kwpsc.html
# 狐妖小红娘的播放页和详情页
https://v.qq.com/x/cover/0sdnyl7h86atoyt.html
https://v.qq.com/detail/0/0sdnyl7h86atoyt.html

从上面两对url中我们不难看出其中的规律。所以我们在第二步解析出国漫播放页的url之后,经过处理,就可以直接得到详情页的url。

备注:上面对腾讯视频的爬取分析仅做流程参考,实际开发可能涉及异步请求等方面的知识。

存储模块

爬取的数据只有存储下来,爬虫才变得更有意义。

通常爬取数据格式有文本、图片等,这里先看图片如何下载并保存到本地目录。

图片下载

之前我用scrapy内置的ImagesPipeline下载GIF动图的时候,折腾了老半天,下载下来的还不是动图。于是回归原始,终成功,一行代码慰平生。

代码如下:

urllib.request.urlretrieve(imageUrl, filename)

所以,不论以后你用原生爬虫还是scrapy的时候,下载图片就记住一行代码就行了!

找个图片链接测试一下:

右键图片,选择拷贝图像地址。

将图片地址放入程序中,代码如下:

import urllib.request
urllib.request.urlretrieve('http://puui.qpic.cn/vcover_vt_pic/0/m441e3rjq9kwpsc1607693898908/0', './1.jpg')

我将图片下来,保存到当前目录并命名为1.jpg,运行程序。

文本数据

  1. 存放于文件中
with open("/path/file.txt", 'a', encoding='utf-8') as f:
f.write(data + '\n')
  1. 使用pymsql模块将数据存放到MySQL的数据表中

  2. 使用pandas或者xlwt模块将数据存放到excel中

结语

本篇文章主要写了一下自己对爬虫程序模块设计的理解,也是对爬虫基础知识的一个总结和收尾。期待下一次相遇。


写的都是日常工作中的亲身实践,置身自己的角度从0写到1,保证能够真正让大家看懂。

文章会在公众号 [入门到放弃之路] 首发,期待你的关注。

爬虫入门到放弃系列05:从程序模块设计到代理IP池的更多相关文章

  1. 爬虫入门到放弃系列07:js混淆、eval加密、字体加密三大反爬技术

    前言 如果再说IP请求次数检测.验证码这种最常见的反爬虫技术,可能大家听得耳朵都出茧子了.当然,也有的同学写了了几天的爬虫,觉得爬虫太简单.没有啥挑战性.所以特地找了三个有一定难度的网站,希望可以有兴 ...

  2. 爬虫入门到放弃系列02:html网页如何解析

    前言 上一篇文章讲了爬虫的概念,本篇文章主要来讲述一下如何来解析爬虫请求的网页内容. 一个简单的爬虫程序主要分为两个部分,请求部分和解析部分.请求部分基本一行代码就可以搞定,所以主要来讲述一下解析部分 ...

  3. php从入门到放弃系列-01.php环境的搭建

    php从入门到放弃系列-01.php环境的搭建 一.为什么要学习php 1.php语言适用于中小型网站的快速开发: 2.并且有非常成熟的开源框架,例如yii,thinkphp等: 3.几乎全部的CMS ...

  4. [大数据从入门到放弃系列教程]第一个spark分析程序

    [大数据从入门到放弃系列教程]第一个spark分析程序 原文链接:http://www.cnblogs.com/blog5277/p/8580007.html 原文作者:博客园--曲高终和寡 **** ...

  5. [大数据从入门到放弃系列教程]在IDEA的Java项目里,配置并加入Scala,写出并运行scala的hello world

    [大数据从入门到放弃系列教程]在IDEA的Java项目里,配置并加入Scala,写出并运行scala的hello world 原文链接:http://www.cnblogs.com/blog5277/ ...

  6. php从入门到放弃系列-04.php页面间值传递和保持

    php从入门到放弃系列-04.php页面间值传递和保持 一.目录结构 二.两次页面间传递值 在两次页面之间传递少量数据,可以使用get提交,也可以使用post提交,二者的区别恕不赘述. 1.get提交 ...

  7. php从入门到放弃系列-03.php函数和面向对象

    php从入门到放弃系列-03.php函数和面向对象 一.函数 php真正的威力源自它的函数,内置了1000个函数,可以参考PHP 参考手册. 自定义函数: function functionName( ...

  8. php从入门到放弃系列-02.php基础语法

    php从入门到放弃系列-02.php基础语法 一.学习语法,从hello world开始 PHP(全称:PHP:Hypertext Preprocessor,即"PHP:超文本预处理器&qu ...

  9. K8S从入门到放弃系列-(16)Kubernetes集群Prometheus-operator监控部署

    Prometheus Operator不同于Prometheus,Prometheus Operator是 CoreOS 开源的一套用于管理在 Kubernetes 集群上的 Prometheus 控 ...

随机推荐

  1. 考研路茫茫——单词情结 HDU - 2243 AC自动机 && 矩阵快速幂

    背单词,始终是复习英语的重要环节.在荒废了3年大学生涯后,Lele也终于要开始背单词了. 一天,Lele在某本单词书上看到了一个根据词根来背单词的方法.比如"ab",放在单词前一般 ...

  2. Dapr微服务应用开发系列3:服务调用构件块

    题记:这篇开始逐一深入介绍各个构件块,从服务调用开始 原理 所谓服务调用,就是通过这个构件块让你方便的通过HTTP或者gRPC协议同步调用其他服务的方法,这些方法也是通过HTTP或者gRPC来暴露的. ...

  3. 敏捷史话(六):也许这个人能拯救你的代码 —— Robert C. Martin

    Robert C. Martin( 罗伯特·C·马丁),作为世界级软件开发大师.设计模式和敏捷开发先驱.C++ Report杂志前主编,也是敏捷联盟(Agile Alliance)的第一任主席,我们尊 ...

  4. 微信小程序开发抖音去水印功能

    之前找了很多抖音去水印的工具全是广告,所以索性自己写了一个,提供给大家免费试用以下是微信小程序的二维码 使用教程: 1.打开微信搜索小程序:沸点软件技术服务 2.打开沸点软件技术服务小程序 3.去抖音 ...

  5. TensorFlow+restore读取模型

    # 注意和前一或二篇Lenet训练并验证的文章从`y_conv = tf.nn.softmax(fc2)`起的不同 # 部分函数请参照前后2篇文章 import tensorflow as tf im ...

  6. involution 内卷化

    involution 内卷化 虽然不熟悉 involution,但是我想起另外两个常用词:evolution(进化)和 revolution(革命). 它们共同的词根volute,拉丁语原意是&quo ...

  7. 手把手教你使用 js 实现一个 Canvas 编辑器

    手把手教你使用 js 实现一个 Canvas 编辑器 拖拽 缩放,等比缩放 导出 image 模版 撤销,重做 OOP,封装,继承,多态 发布库 CI/CD (gitlab/github) ... h ...

  8. CSS Layout All In One

    CSS Layout All In One CSS2 position float % px , rem, em CSS3 flex grid multi column vw / vh 常见布局模式 ...

  9. element ui 停止维护了

    ️‍♂️ element ui 停止维护了 最近看到有人说 element ui 已经停止维护了,还有点不相信; 不过到 github 验证一下,好像是真的呀 4 个月,没有任何更新了 https:/ ...

  10. MacBook Pro 2019 13 inch & screen blink

    MacBook Pro 2019 13 inch & screen blink MacBook Pro 闪屏 https://macreports.com/mac-how-to-trouble ...