一、问题描述
网站的反爬措施越来越严格,各种反反爬措施也不断发展,最有效的反爬措施无异于是通过代理ip池的方法。
但是对于学习者或者数据量并不大的同学来说,购买或者维护一个ip池的成本并不低。
由此产生了一个想法,作为迫不得已的代替方法。
二、解决思路
现在宽带拨号使用的都是动态ip,每次重新拨号,就可以获得一个新的ip,这样就可以勉强解决上述问题。
头铁的解决思路
- 将要爬取的链接用列表进行存储,
- 然后大概判断一下几次请求之后就会被封掉(如:单个ip只能连续爬取20次)
- 然后以20个为一组,将列表进行拆分,并保存成新的文件,这样就获得了若干个文件
- 每次爬取一个文件之后,重新拨号获取新ip,然后进行重复爬取工作
存在的问题:
这样做太麻烦了,跟手工爬取没啥两样了,且重复性动作太多,文件数量也多,容易出错。
改进之后的思路:
- 将请求列表保存到文件中
- 读取请求列表文件,重新恢复成列表
- 读取索引文件,获得已爬取的数据
- 按照列表索引进行请求数据
- 如果列表url已经爬取过,则跳过
- 如果程序运行中断,则将已爬取的索引字典进行重新保存
- 重新拨号,继续爬取
伪代码如下:
import requests
class Content:
def __init__(self):
self.url_list = [] # 存放已经爬取过的url 用字典存取也可以,效率更高。
def clear(self):
with open("索引url", mode="r") as f: # 将已经爬取过的url进行存储
for line in f:
url = line.strip()
self.url_list.append(url)
with open("索引url", mode="a") as f:
with open("url列表文件", mode='r') as fr:
for line in fr:
url = line.strip()
if url not in self.url_list: # 判断是否已经被爬取过
try:
status_code = self.download_url(url)
if status_code == 1: # 判断爬取状态
f.write(url)
f.write('\n')
else:
break
except Exception as e:
print(e)
break
def download_url(self, url):
resp = requests.get(url)
resp.encoding = "utf-8" # 这里根据网页编码进行处理
if "百度安全验证" in resp.text: # 以百度贴吧为例,爬取次数过多会提示安全验证
resp.close()
print("爬取次数过多,请更换ip")
return 1
else:
print(resp.text) # 输出或者保存结果
resp.close()
return 0
if __name__ == "__main__":
c = Content()
c.clear()
三、后记
这是我根据增量式爬虫,利用redis数据库的去重功能,自己琢磨的一个笨办法,也是无奈之举。ip代理一直不成功,难办的很。
- 一开始是用计数的方式进行的,但是这种计数的方法特别繁琐,且不能适用于异步协程
- 现在利用json进行保存,就会显得比较简单且不用频繁读取文件,并能够适用到异步协程
依然存在的问题:
- 如果某几个url一直无法爬取,则到最后一直重复运行程序但未能产生有效数据(可以尝试增加爬取次数信息,通过爬取次数判断是否需要继续爬取,与爬虫自省稍微有所不同,但是原理是一样的)
- 受ip段的限制,拨号本身可能并不能获取更多的有效ip
- 如果拨号太频繁,可能会导致拨号错误
如果还有更好的办法,欢迎评论告诉我,感谢各位~