前言
最近在爬取知乎的资料时,无奈在测试的时候一直频繁访问,导致IP被封(被封的提示为:))于是在多次提取无果以后,如果购买代理不划算啊,刚好最近学了mysql数据库,为何不自建一个小型的ip代理池呢,顺便做个小项目练练手,供个人获取数据应该是够了,自力更生,丰衣足食。废话就不说了,
直接上正文
一、寻找代理网站提供的几十个免费代理 。像西刺代理,百度一抓一大把,一般选择一个就行了,选择一个合适的start_url地址就可以了
二.发送请求,并获取element 类型的html,方便使用xpath提取(直接看代码)
def parse_url(self,url):
'''一个发送请求,获取响应,同时etree处理html'''
response = requests.get(url,headers=self.headers,timeout=10) #发送请求
html = response.content.decode() #获取html字符串 为什么不能直接html = response.text 一样得到的是html字符串啊?
html_etree = etree.HTML(html) #获取element 类型的html
return html_etree
三.提取数据(提取如123.161.160.74:8080形式的good_proxy,,服务器地址,验证时间,存活时间)内容如图:
代码如下:
total_good_ip = []
tr_list = html_etree.xpath("//tr[@class='odd'] | //tr[@class='']" ) # 获取的数量增加一倍,到达100个
print("="*100)
print(len(tr_list))
for tr in tr_list:
item= {}
proxy_value= tr.xpath("./td[2]/text()")[0]+ ":" + tr.xpath("./td[3]/text()")[0] #123.161.160.74:8080形式
#print(proxy_value)
# 如果IP有效就加入到列表
good_proxy = self.test_proxy_value(proxy_value) # 传入字符串形式数据
#print(good_proxy)
if good_proxy is not None:
item["good_proxy"] = good_proxy # 123.161.160.74:8080形式的可用IP
if tr.xpath("./td[4]/text()") == [] :
item["ip_location"] = "无"
else:
item["ip_location"] = tr.xpath("./td[4]/text()")[0]
item["test_time"] = tr.xpath("./td[8]/text()")[0] # 验证时间
item["live_time"] = tr.xpath("./td[7]/text()")[0] # 存活时间
total_good_ip.append(item)
#print(item)
# 4.保存到本地
# self.save2txt(item) # 如果保存到本地,IP地址不方便去重,不过可以保存成CSV格式文件
# 5.保存到数据库,
self.save2mysql(item)
注意事项:
# 获取的数量增加一倍,到达100个 : tr_list = html_etree.xpath("//tr[@class='odd'] | //tr[@class='']" )
2.验证该ip地址是否有效: good_proxy = self.test_proxy_value(proxy_value) # 传入字符串形式数据
代码如下(随便找了几个网址做测试,后期可以建一个类似的池,像政府,教育之类的网址一般比较好找):
def test_proxy_value(self,proxy): # 传入字符串形式的参数
try:
proxy_host = proxy
protocol = 'https' if 'https' in proxy_host else 'http'
proxies = {protocol: proxy_host}
test_url_list=["https://hao.360.cn/?src=bm","https://www.baidu.com","https://www.163.com/","https://www.taobao.com/","https://www.tongji.edu.cn/","http://www.ruc.edu.cn/","http://www.bjfu.edu.cn",
"https://www.sina.com.cn/","https://www.jd.com/","http://www.qq.com/","http://www.meituan.com/"]
test_url = random.choice(test_url_list) # choice 传入的参数是列表
print(test_url) # 看此时的测试网址,确实是随机变化的!!
response = requests.get(test_url,headers=self.headers, proxies=proxies, timeout=3)
if response.status_code != 200:
return None
else:
return proxy
except Exception as e:
print(e)
print("*"*100) #测试错误信息
return None
3.注意字符串,列表,元组的相互转化:
四。保存到mysql数据库
代码如下:
def save2mysql(self,item):
conn = pymysql.connect(host='localhost',port =3306,database ="IP_POOL" ,user='root',password='********',charset='utf8')
cur = conn.cursor()
# 首先应该先创建ip_pool库,然后在创建ip_list表
print(item)
#创建iP_list表 #下面创建语句,执行一次就可以了,后期如果再执行会报错
# sql = """
# create table ip_list(
# id int unsigned primary key auto_increment not null,
# good_proxy varchar(40) not null,
# ip_location varchar(40) not null,
# live_time varchar(20) not null,
# test_time varchar(20) not null)DEFAULT CHARSET=utf8;
# """
# mysql添加语句(1.先检查该ip地址是否重复。 2.不重复就添加)
sql_judge = """select good_proxy from ip_list where good_proxy=(%s)"""
count_num = cur.execute(sql_judge,(item["good_proxy"]))
print("查询到{}有{}条数据:".format(item["good_proxy"],count_num))
if count_num > 0:
print("已经有该IP地址了")
else:
sql_add = '''
insert into ip_list(good_proxy,ip_location,live_time,test_time) values(%s,%s,%s,%s);
'''
try:
count = cur.execute(sql_add,(item["good_proxy"],item["ip_location"],item["live_time"],item["test_time"]))
print(count)
conn.commit()
print("添加数据成功")
except Exception as error:
print(error)
conn.commit() # 确认再次提交一次
cur.close()
conn.close()
到这里其实ip代理池的项目算是可以基本使用了,测试的结果如下图:
虽然代码在添加之前会有good_proxy的是否重复的判断:
,但是可能会存在一个问题:前期已经存在的ip地址失效了
解决措施:1.应该可以再建立一个函数,对所有已经存在的IP地址进行遍历检测是否还可以正常使用(放在最后面在执行程序)!
代码如下:
def check_mysql(self):
conn = pymysql.connect(host='localhost', port=3306, database="IP_POOL", user='root', password='******',charset='utf8') # 这里是否要重新建立数据库呢?
cur = conn.cursor()
# mysql先提取数据,进行判断IP有效性
count_total = cur.execute("select good_proxy from ip_list")
print("测试:查询到%d条数据:" % count_total) # 得到元组形式的信息
# test_total_num = cur.fetchall() # 这种测试是可以提取所有数据的
# print(test_total_num)
pur_count_tuple_list = cur.fetchall() # 得到一个元组 ,cur.fetchone()为什么有时候取到的是None类型
for pur_count_tuple in pur_count_tuple_list:
# 获取查询的结果
# 打印查询的结果
print("测试:得到一个{}元组类型".format(pur_count_tuple))
if pur_count_tuple !=None:
pur_count_str = "".join(list(pur_count_tuple)) # 转换为字符串
print("测试:得到一个{}字符串类型".format(pur_count_str))
good_proxy = self.test_proxy_value(pur_count_str)
# 获取查询的结果
if good_proxy ==None: # 该IP地址失效,就删除该IP地址所在的行
# 删除对应的IP地址之后,剩下的数据应该自动补上id号
try:
count = cur.execute( 'delete from ip_list where good_proxy=pur_count_tuple') # pur_count_tuple 还是pur_count_str呢?
conn.commit()
print("删除无效IP成功")
except Exception as error:
print(error)
else:
print("该{}地址有效".format(good_proxy))
conn.commit() # 多添加一次确认提交
cur.close()
conn.close()
2.或许也可以每次使用ip代理的时候先把之前的iP_list表给删除,每次都重建一个,使用Navicat也许操作比较方便
总结:
1.该小型ip代理池,满足日常学习获取数据的要求,对爬虫学习的很有帮助。比如基本爬虫的经典四步法有了更加清楚的认识,对xpath获取网页数据更加熟练,对保存到mysql数据库(增删改查,去重),对基本数据类型的理解,也可以保存到本地并读取数据,!此外随时使用的时候就可以运行,减少金钱成本,没必要保持一直动态进行筛选!项目的具体资料:
https://download.youkuaiyun.com/download/qq_33125039/10636941
2.获取的ip地址也可以保存在本地,然后在从本地读出,但是注意文件的格式
3.大家有什么好的想法和意见欢迎留言,多多交流,以上只是个人的理解,如有错误,还望大神指出,感谢!本人QQ:1143132860
4.大致参考资料:
(1)https://blog.youkuaiyun.com/anya/article/details/6407280/