之前写过一篇python实战项目二:获取IP代理的文章,不过说实话,这个程序有几点不足,以至于不能愉快玩耍之后,我就重新整理了思路,又写了一个关于获取免费IP代理的代码。在这儿我想写反思一下之前这个代码的主要不足:
第一点,由于数据很杂,所以在提取信息时频繁的使用了循环,但是循环使用的太频繁会使得程序执行的速度效率降低,而字典的索引效率就高效的多,所以这一次不必使用循环的地方就不使用循环,改用字典。
第二点,爬取下来的IP不是都能用的,所以得检验一下。我之前用的方法就是直接将得到的IP访问某个网站,若状态码status_code为200,则说明可用。但是实际上得到的IP不能用的还是很多。所以这次我增加了一些检验的条件。
这次我还是选择西刺代理。接下来就开始说一下过程:
首先先导入相关的模块
import requests
from lxml import etree
import re
import time
然后定义函数,爬取网页信息
def get_html(url):
# 获取西刺代理高匿代理的每一页的html,return: 返回html
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'}
try:
response = requests.get(url, headers=headers)
if response.status_code == 200:
return response.text
else:
print('IP不能用')
except Exception as e:
print(e)
得到响应后,就是要提取页面的信息了,先用xpath表达式得到信息,包括IP地址、是否匿名、类型、连接时间、存活时间等,提取之后的内容用字典来盛装,然后返回该字典
def get_data(html):
# 获取每一页的IP信息, return:返回这一页IP的信息(封装成字典形式)
info = etree.HTML(html)
try:
address = info.xpath('//tr[@class="odd" or class=""]/td[2]/text()') # IP地址
ports = info.xpath('//tr[@class="odd" or class=""]/td[3]/text()') # 端口
anonymous = info.xpath('//tr[@class="odd" or class=""]/td[5]/text()') # 匿名形式
http_https = info.xpath('//tr[@class="odd" or class=""]/td[6]/text()') # http or https
speed = info.xpath('//tr[@class="odd" or class=""]/td[7]/div[1]/@title') # 连接速度
speed_width = info.xpath('//tr[@class="odd" or class=""]/td[7]/div[1]/div/@style') # 连接速度的比例
conn_time = info.xpath('//tr[@class="odd" or class=""]/td[8]/div[1]/@title') # 连接时间
conn_time_width = info.xpath('//tr[@class="odd" or class=""]/td[8]/div[1]/div/@style') # 连接时间的比例
life = info.xpath('//tr[@class="odd" or class=""]/td[9]/text()') # 存活时间
test = info.xpath('//tr[@class="odd" or class=""]/td[10]/text()') # 检验时间
data_info = {}
# print(len(address))
for i in range(0, len(address)):
data_info[str(i+1)] = {'IP地址': address[i]+':'+ports[i],
'是否匿名': anonymous[i],
'类型': http_https[i],
'速度': eval((re.compile('(.*?)秒').findall(speed[i]))[0]),
'速度比例': eval((re.compile('width:(.*?)%').findall(speed_width[i]))[0]),
'连接时间': eval((re.compile('(.*?)秒').findall(conn_time[i]))[0]),
'耗时比例': eval((re.compile('width:(.*?)%').findall(conn_time_width[i]))[0]),
'存活时间': eval((re.compile('(\d+).*?').findall(life[i]))[0]),
'验证时间': test[i]}
return data_info # 返回名为data_info的字典,字典的每个键值对就是一个IP的信息{{'1':{}},{'2':{}}}
except Exception as er:
print(er)
接下来就将上面得到的字典信息存入文件,存入之前要先进行第一次的检验,就是筛选出存活时间>100天,还有速度等限制条件的,这样至少可以保证得到的IP生命力是比一般的那些顽强。
def save_to_file(data):
# 将得到的符合要求的IP写入文件;param data: 所有的IP集合(字典形式的)
try:
# print(data['1'])
# print(type(data['2']['耗时比例']), type(data['2']['存活时间']))
with open('IP代理池.csv', 'w', encoding='utf-8') as f:
for i in range(0, len(data)):
if data[str(i+1)]['速度比例'] >= 70 and data[str(i+1)]['耗时比例'] >= 75 and \
data[str(i+1)]['存活时间'] >= 100:
# 存活时间要>=100天,速度和耗时也有相关的限制
f.write(str(data[str(i+1)]))
f.write('\n')
else:
pass
except Exception as er:
print(er)
然后就是主函数,在这个函数中实现调用其他函数,实现整个程序的功能,因为只是用来检验能否用该方法得到有效的IP,所以我的循环range(1, 2)只爬取了一页的内容。
def main():
# 主逻辑函数,实现得到IP的功能;param page: 默认为爬取1页,其他函数调用时刻自行改变该值;return: None
base_url = 'http://www.xicidaili.com/nn/'
for i in range(1, 2):
url = base_url + str(i)
print('爬取第{}页'.format(str(i)).center(20, '='))
html = get_html(url) # 得到当前爬取页面的html信息
data = get_data(html) # 解析当前页面,得到想要获取的信息
save_to_file(data) # 将得到的信息写入文件保存
if i % 2 == 0: # 每爬取两页,停3秒
time.sleep(3)
到这儿整个程序执行之后就可以得到有效的IP了,部分结果如下图
但是这只得到相对存活率高一点的IP,有些IP可能已经被一些网站加入黑名单了。那么我得到这个IP代理池之后,我怎么使用呢?就是检验的第二重,用带爬取的网站去检验,检验刚刚得到的文件中的那些有效IP,用这些IP去访问待爬取的网页,若返回状态码是200,则说明还能用,不能用的直接移除就行。
两种方法,一种是你在运行你的其他爬虫的时候,先将该程序运行一遍,得到有效IP,然后在那个爬虫里读取这个程序结束后的装有效代理IP的文件就行;另一种方法就是在这个程序中写一个函数,作为那个函数调用的接口,在那个程序中导入这个程序,然后运行的结果就是会先执行这个程序,函数返回的是一个有效IP的列表,在那个爬虫里直接使用random模块的choice方法就可以随机得到一个该列表的一个IP,每次执行得到IP的都是一个随机的,也会更好的防止被封。两种方法其实差不多,只不过第二种方法会将该爬虫获取有效IP代理的时间也会在那个程序中消耗,所以这个就看个人选择。我写的接口是
def get_ip():
# 接口函数,其他爬虫可以直接导入这个程序(模块)之后调用该方法就行,返回的就是有效的IP列表
test_url = "https://blog.csdn.net/"
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36\
(KHTML, like Gecko) hrome/68.0.3440.106 Safari/537.36'}
ip_data = []
try:
with open('IP代理池.csv', 'r', encoding='utf-8') as f:
for item in f.readlines():
ip_data.append(eval(item)['IP地址']) # 读取文件并将IP全部提取出来检验
except Exception as e:
print(e)
# print(ip_data)
for data in ip_data:
proxy = {'http': 'http://'+data}
try:
r = requests.get(test_url, headers=headers, proxies=proxy)
if r.status_code == 200:
pass # 若此IP有效,则万事大吉
# print('{}可用'.format(proxy).center(20, '='))
else:
ip_data.remove(data) # 若不可用,则移除
# print('{}不可用'.format(proxy).center(20, '%'))
except Exception as e:
print(e)
# print('得到{}个有用个IP'.format(len(ip_data))
return ip_data