Python爬虫实战——搭建自己的IP代理池[通俗易懂]

Python爬虫实战——搭建自己的IP代理池[通俗易懂]如今爬虫越来越多,一些网站网站加强反爬措施,其中最为常见的就是限制IP,对于爬虫爱好者来说,能有一个属于自己的IP代理池,在爬虫的道路上会减少很多麻烦环境参数工具详情服务器Ubuntu编辑器Pycharm第三方库requests、bs4、redis搭建背景之前用Scrapy写了个抓取新闻网站的项目,今天突然发现有一个网站的内容爬不下来…

大家好,又见面了,我是你们的朋友全栈君。

如今爬虫越来越多,一些网站网站加强反爬措施,其中最为常见的就是限制IP,对于爬虫爱好者来说,能有一个属于自己的IP代理池,在爬虫的道路上会减少很多麻烦

爬虫实战——搭建自己的IP代理池

环境参数

工具 详情
服务器 Ubuntu
编辑器 Pycharm
第三方库 requests、bs4、redis

搭建背景

之前用Scrapy写了个抓取新闻网站的项目,今天突然发现有一个网站的内容爬不下来了,通过查看日志发现是IP被封,于是就有了这篇文章。

思路

一般出售IP代理的都会提供一些免费代理,既然是免费的就不要浪费,我们只要把免费的代理爬下了,及时维护和更新就可以把免费的变成我们自己的代理池

编写爬虫

搜索免费代理会有很多结果,一般情况大部分都可以使用,这里以其中一家代理为例,打开代理网站以后,首先通过浏览器查看代码,然后分析代码开始编写爬虫

网站源代码

<!--其中一条数据-->
...
<tr class="success">
<td class="ip"><div style="display:inline-block;"></div>
<span style="display:inline-block;">59</span><span style="display:inline-block;">.1</span><div style="display:inline-block;"></div><p style="display:none;">0</p><span>0</span><span style="display:inline-block;"></span><span style="display:inline-block;">8.</span><div style="display:inline-block;">12</div><span style="display:inline-block;">5</span><p style="display:none;"></p><span></span><p style="display:none;"></p><span></span><div style="display:inline-block;">.2</div><div style="display:inline-block;">41</div>:<span class="port GEGEA">8080</span></td>
<td><a title="高匿代理IP" style="color:red;" class="href">高匿</a></td>
<td><a title="http代理IP" class="href">http</a></td>
<td><a title="中国代理IP" class="href">中国</a>&nbsp;&nbsp;
<a title="北京代理IP" class="href">北京</a>&nbsp;&nbsp;
<a title="北京代理IP" class="href">北京</a> </td><td><a title="方正宽带代理IP" class="href">方正宽带</a></td>
<td>2.786 秒</td><td>7分钟前</td><td style="color: green; font-weight: bold;">11天</td></tr>
....

通过上面一条数据可以看出,提供者为防止网站被爬取还是做了一些防范措施,但是我们可以使用正则表达式取出IP地址和端口号。
使用正则表达式的时候我们一般会有两种思路

  • 1.提取数字和点.
  • 2.过滤html标签,保留我们想要的数字和点

这里我们以第二种方法为例

soup = BeautifulSoup(html, 'html.parser')
data = soup.find('td', class_='ip')
res=re.compile('<p.*?/p>|<.*?>',re.S)
proxy=re.sub(res, '', str(data))
print(proxy)
# 59.108.125.241:8080

这个时候IP地址和端口号就提取出来了,当你把整个网页的代理地址都提取出来以后,你会发现没有一个可以使用的。

这是为什么呢?难道是代理商提供的免费代理都是垃圾,其实不然,细心的你可能会发现你匹配的端口和他们官网显示的端口号不一样,很显然他们的端口号是通过js动态加载的,遇到这种情况,我们一般也会想到2种解决方案

  • 使用selenium
  • 破解js
    如果加密方式复杂、js文件很多,无从下手时可以使用selenium,好在我们今天爬取的这个网站js文件不是很多,通过打断点,很容易定位到我们要解密的js文件,下面我就分享一下的我解决方法

破解js

  • 找到对应的js文件

可以给网站中的每个js文件打断点,一步步调试找出影响数据的js文件,通过调试我找到这样一个文件

eval(function(p,a,c,k,e,d){ 
e=function(c){ 
return(c<a?"":e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){ 
while(c--)d[e(c)]=k[c]||e(c);k=[function(e){ 
return d[e]}];e=function(){ 
return'\\w+'};c=1;};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p;}('1M(17(p,a,c,k,e,r){e=17(c){18(c<a?\'\':e(1w(c/a)))+((c=c%a)>1s?1b.1r(c+1q):c.1v(1u))};19(!\'\'.1a(/^/,1b)){1c(c--)r[e(c)]=k[c]||e(c);k=[17(e){18 r[e]}];e=17(){18\'\\\\w+\'};c=1};1c(c--)19(k[c])p=p.1a(1t 1y(\'\\\\b\'+e(c)+\'\\\\b\',\'g\'),k[c]);18 p}(\'i h$=[\\\'\\\\E\\\\n\\\\x\\\\s\\\\j\\\',"\\\\l\\\\m\\\\v\\\\o","\\\\o\\\\j\\\\G\\\\p","\\\\r\\\\q\\\\H\\\\l\\\\I\\\\J\\\\K",\\\'\\\\M\\\',"\\\\m\\\\j\\\\j\\\\s",\\\'\\\\v\\\\p\\\\m\\\\k\\\\k\\\',"\\\\k\\\\n\\\\p\\\\r\\\\j","\\\\O","","\\\\p\\\\l\\\\q\\\\Q\\\\j\\\\o","\\\\n\\\\R\\\\k\\\\o",\\\'\\\\S\\\\T\\\\V\\\\z\\\\A\\\\B\\\\C\\\\D\\\\u\\\\F\\\',"\\\\n\\\\m\\\\s\\\\k\\\\l\\\\u\\\\q\\\\j","\\\\16\\\\x\\\\r\\\\q",\\\'\\\'];$(y(){$(h$[0])[h$[1]](y(){i a=$(t)[h$[2]]();L(a[h$[3]](h$[4])!=-w){N};i b=$(t)[h$[5]](h$[6]);P{b=(b[h$[7]](h$[8]))[w];i c=b[h$[7]](h$[9]);i d=c[h$[10]];i f=[];U(i g=W;g<d;g++){f[h$[11]](h$[12][h$[3]](c[g]))};$(t)[h$[2]](X[h$[13]](f[h$[14]](h$[15]))>>Y)}Z(e){}})})\',1A,1B,\'|||||||||||||||||1C|1x|1z|1p|1h|1i|1d|1e|1f|1m|1n|1o|1g|1k|1l|1j|1W|17|1X|1Y|1V|1S|1T|1U|1Z|23|25|24|20|21|19|22|18|1R|1H|1I|1J|1G|1D|1E|1F|1O|1P|1Q|1N|||||||1K\'.1L(\'|\'),0,{}))',62,130,'|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||function|return|if|replace|String|while|x70|x68|x6c|this|x65|x61|0x1|x49|x63|x6e|x69|x72|x73|29|fromCharCode|35|new|36|toString|parseInt|var|RegExp|x74|62|69|_|x42|for|x43|x41|try|x67|x75|x6a|split|eval|catch|0x0|window|0x3|x20|x47|x48|x2e|x46|x6f|x44|x45|x5a|x4f|x66|x2a|x6d|x78|x64'.split('|'),0,{ 
}))
  • 解密js文件

很显然上面这个文件是加密压缩过的,通过在线解密工具,两次解密以后我们得到这样一个方法,有点js基础的同学应该能看的懂,但是还是不够直观,因为这个方法首先定义了一个数组,每个变量都是用数组切片的方式代替,所以一眼很难看出加密方式

var _$ = ['\x2e\x70\x6f\x72\x74', "\x65\x61\x63\x68", "\x68\x74\x6d\x6c", "\x69\x6e\x64\x65\x78\x4f\x66", '\x2a', "\x61\x74\x74\x72", '\x63\x6c\x61\x73\x73', "\x73\x70\x6c\x69\x74", "\x20", "", "\x6c\x65\x6e\x67\x74\x68", "\x70\x75\x73\x68", '\x41\x42\x43\x44\x45\x46\x47\x48\x49\x5a', "\x70\x61\x72\x73\x65\x49\x6e\x74", "\x6a\x6f\x69\x6e", ''];
$(function() { 

$(_$[0])[_$[1]](function() { 

var a = $(this)[_$[2]]();
if (a[_$[3]](_$[4]) != -0x1) { 

return
};
var b = $(this)[_$[5]](_$[6]);
try { 

b = (b[_$[7]](_$[8]))[0x1];
var c = b[_$[7]](_$[9]);
var d = c[_$[10]];
var f = [];
for (var g = 0x0; g < d; g++) { 

f[_$[11]](_$[12][_$[3]](c[g]))
};
$(this)[_$[2]](window[_$[13]](f[_$[14]](_$[15])) >> 0x3)
} catch (e) { 
}
})
})

通过对数组的拆分,你会发现上面方法的核心内容可以简化成这样

    var f = []; 
var c="GEGEA".split(""); 
for (var g = 0; g < c.length; g++) { 

f.push('ABCDEFGHIZ'.indexOf(c[g]))
};

我来解释一下这个代码片段,首先”GEGEA”这个值是怎么来的? 这个值不是固定的,而是网页源码中class=‘port GEGEA’ port的同级class,获取到这个class以后,先把它转为数组,判断数组中的每个元素在’ABCDEFGHIZ’中的位置,会得到一个类似这样的数组[6, 4, 6, 4, 0],再把这个新数组转为字符串,然后位移,就可以得到真实的端口号,所以可以把解密函数简化成这样

    // 定义一个数组,用于记录class在'ABCDEFGHIZ'出现的位置
var f = []; 
// 把class转为一个数组
var c="GEGEA".split(""); 
// 根据数组的长度记录数组中每个元素在'ABCDEFGHIZ'出现的位置
for (var g = 0; g < c.length; g++) { 

f.push('ABCDEFGHIZ'.indexOf(c[g]))
};
// 把数组转为字符串,再进行运算
var port=f.join('')>>0x3
// 得到真实的端口号
console.log(port)

如果上面的js解密步骤你已经理解,接下来用python重写一下这个解密步骤很会简单很多,具体代码如下所示

# port_class 是源代码port的同级class
def parse_port(self,port_class):
string = 'ABCDEFGHIZ'
arr = list(port_class)
lists = []
for x in range(0, len(arr)):
lists.append(string.find(arr[x]))
ports = ''.join(str(x) for x in lists)
return int(ports) >> 3

这段python代码和上面的js代码逻辑一致,效果也一样,只不过是用python翻译了一遍。

到这里爬虫的难点我们都解决了,现在要做的是把爬取下来的代理存储到redis里面。
至于为什么用redis存储,有以下几点原因:

  1. redis相比mysql、文件写入速度更快
  2. 使用redis里的集合特性,不用担心有重复数据
  3. 项目中使用了分布式爬虫,存到redis中方便多台服务器调用

IP代理池添加和维护

下面分为4个步骤来分享一下IP代理池的维护

  • 安装redis
    不同系统redis的安装方法不同,本文以Ubuntu为空
apt-get install redis-server

redis 安装好以后会自动安装一个客户端redis-cli,我们可以通过redis-cli对数据的增删改查,比如:

# 进入客户端
redis-cli
# 添加一条数据
set name 'hello world'
# 获取name的值
get name

但是我们总不能把IP代理地址一个个手动添加到redis里,所以我们还要安装一个python操作redis的模块

这个模块名刚好也叫redis

pip install redis

模块安装好我们就可以通过python管理redis里的数据了
redis有5种数据类型分别为:string(字符串),hash(哈希),list(列表),set(集合)及zset(有序集合),我们这里主要使用set(集合)

  • 添加IP代理地址到redis
# 导入模块
import redis
# 连接到Redis服务器
conn = redis.Redis(host='127.0.0.1', port=6379)
# 添加数据 key 可以更加自己的需求设置
conn.sadd('proxy','119.179.0.1:8083')
# 随机取出一条代理地址
conn.redis.srandmember('proxy')
  • 验证IP代理是否有效
    可以在存入的时候可以验证,也可以在取的时候验证代理是否有效,但是如果存入的时候就验证,取得时候有可能已经不能使用
# 导入模块
import redis
import requests
# 连接到Redis服务器
conn = redis.Redis(host='127.0.0.1', port=6379)
# 随机取出一条代理数据
ip=conn.redis.srandmember('proxy')
print(ip)
url='https://www.baidu.com'
proxies = { 

"http": "http://" + ip.decode("utf-8")
}
# 使用IP代理访问百度,测试代理地址是否有效
try:
data = requests.get(url=url, proxies=proxies, timeout=5)
except:
# 代理地址无效
  • 删除无效的IP代理

验证IP代理是否无效,如果代理地址无效,可以使用以下命令删除代理,这样可以保证我们代理池中的地址都是有效的

conn.redis.srem('proxy', '无效的IP代理地址')

最后把获取代理的步骤封装成一个方法,在需要代理的地方调用即可

到这里我们的代理池就搭建好了,如果感觉只有一个网站的数据不能我们使用,只需要多爬取几个免费代理及时维护就可以啦。

对于大多数爬虫初学者来说,其实爬取一个没有反爬的网站不是什么难事,无非就是把网站的源代码获取下来,然后使用bs4或者正则表达式来提取数据,这里我专门找来一个有反爬的网站,就是想让大家感受一下反爬的流程,当然这也是很简单的一个。

对于代理池的搭建记住三点即可:

  1. 添加IP代理
  2. 验证IP代理是否有效
  3. 及时删除无效代理

总结:本文用一半的篇幅再和大家分享JS破解的步骤,对于没有JS基础的同学看起来会有点吃力,但是通过python的解密步骤,可能会让你对JS加密流程有个大致了解。reids的操作可以参考具体文档

JS在线解密工具

https://tool.lu/js/

参考文档

https://pypi.org/project/redis/

https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#

爬取网站

http://www.goubanjia.com

源代码

https://github.com/iyuyoung/proxy_pool

案例
https://www.mphot.cn
Python爬虫实战——搭建自己的IP代理池搭建自己的IP代理池

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/143738.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 不会部署并调试SpringBoot源码?一看必会IDEA操作[通俗易懂]

    不会部署并调试SpringBoot源码?一看必会IDEA操作[通俗易懂]SpringBoot现在这么火几乎是面试必问,如果你能说出点源码是如果实现的,那么面试官跟定对你刮目相看。但是好多同学往往在部署SpringBoot源码的时候,由于找不到门路而选择放弃。本篇博客通过简单方式教你如果将SpringBoot源码导入IDEA并对其进行调试,对核心的代码添加自己注释内容。

  • java缓存技术的介绍

    java缓存技术的介绍一、什么是缓存1、Cache是高速缓冲存储器一种特殊的存储器子系统,其中复制了频繁使用的数据以利于快速访问2、凡是位于速度相差较大的两种硬件/软件之间的,用于协调两者数据传输速度差异的结构,均可称之为Cache二、缓存的分类1、基于web应用的系统架构图2、在系统架构的不同层级之间,为了加快访问速度,都可以存在缓存操作系统磁盘缓存->减少磁盘机械操作

  • Windows程序设计——LoadImage参数及其用法「建议收藏」

    Windows程序设计——LoadImage参数及其用法「建议收藏」LoadImage是一种函数,功能是装载图标,光标,或位图。简介编辑函数功能:该函数装载图标,光标,或位图。函数原型:HANDLE LoadImage(HINSTANCE hinst,LPCTSTR lpszName,UINT uType,int cxDesired,int cyDesired,UINT fuLoad);参数hinst:处理包含被装载图像模块的实例。若要…

  • centos7安装kong网关

    centos7安装kong网关下载安装包:https://bintray.com/kong/kong-community-edition-rpm/download_file?file_path=centos/7/kong-community-edition-1.0.2.el7.noarch.rpm运行下面的两个命令进行安装$sudoyuminstallepel-release$sudoyuminstallkong-community-edition-1.0.2.el7.noarch.rpm–nogpgche

  • ajax 跨域请求api_java跨域请求的三种方法

    ajax 跨域请求api_java跨域请求的三种方法让ajax请求访问servlet,可以让ajax请求跨域,通过直接本应用中的资源servlet,ajax可以不跨域访问本应用中的servlet,让java程序发送Get,Post请求,来访问2号服务器中的的servlet。ProxyServlet发送get请求,访问b站点TargerServlet,请求响应回来数据在进行响应。首先把架包导入创建lib目录,复制进去选中右键Addaslibrary。(2)ajax跨域解决方案之代理机制的代码实现。引入架包httpclient架包。…

  • ubuntu用 vmware 安装win10系统

    ubuntu用 vmware 安装win10系统1,下载VMwareWorkstation14Pro官网:https://www.vmware.com/cn.html需要注册一下才能下载,当然你也可以在其他网站下载。需要下载VMwareWorkstation14.0.0ProforLinux这个版本,下载结束之后的文件是:VMware-Workstation-Full-14.0.0-6661328.x86_64.bu

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号