开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
前言
1.本文重点
本文就关于爬虫代理以及在爬虫过程中可能出现的报错做一个汇总。
如果,感兴趣的话,就继续往下看吧,不感兴趣的快run,开个玩笑
2.参考链接
为什么网站知道我的爬虫使用了代理? - 知乎 (zhihu.com)
3.推广一波
关于爬虫入门,你可以先光顾一下我的专栏,Python学习 - xincheng_q的专栏 - 掘金 (juejin.cn)
专栏内容循序渐进,涉及到的均是爬虫基础内容 主要是难得我也不会,
「Python」爬虫-1.入门知识简介 - 掘金 (juejin.cn)「Python」爬虫-2.xpath解析和cookie,session - 掘金 (juejin.cn)
「Python」爬虫-3.防盗链处理 - 掘金 (juejin.cn)
「Python」爬虫-4.selenium的使用 - 掘金 (juejin.cn)
「Python」爬虫-5.m3u8(视频)文件的处理 - 掘金 (juejin.cn)
「Python」爬虫-6.爬虫效率的提高 - 掘金 (juejin.cn)
「Python」爬虫-7.验证码的识别 - 掘金 (juejin.cn)
代理
关于代理,为什么要用代理?
大多数回答是:假如你疯狂访问同一个网站,并且该网站会检测某一段时间某个IP 的访问次数,最后的可能的结果就是你被禁止访问该网站了。
这里讲一个小故事吧:在学校期间老师要求我们每天都进到一个系统进行安全上报,上报的系统记录了你的登录状态以及你第一次填写的信息,均属于表单的形式。
如果是手动上报的话,每天需要做的就是:打开微信->找到公众号->找到上报入口->漫长的等待(有时候人多会极度卡顿,以至于上报页面一直出不来)->页面加载完成->补充自己的信息->点击提交按钮。
每天都是重复的操作,如果页面不卡顿,我就当是图一乐,每天打卡还能接受,但是它卡顿啊!(抓狂💀, 不行,还是得想办法整一个自动上报的东西。
于是,通过各种方法,白嫖到了自动上报的代码,然后自己刚好有个轻量级服务器,就开了个定时任务,让他每天都执行。
刚开始,一直都非常的顺利,直到有一天,有人戳了戳你,说你今天咋没上报啊,
我去,心想:坏了,不会是程序出问题了吧。(虽然说弄了自动推送短信的功能,但是,由于太懒,每天也没仔细看)进去一看,运行,“Max retries exceeded with url...”
,我一个小白看到这就封了啊,一顿请教+百度,听说是学校把湖北省外的IP都给封了,所以暂且不能用其他地方的IP上报了。
心里又一顿骂骂咧咧...
当你的程序对某一个网站请求过于频繁,并且对方通过某些机制检测到你的行为属于爬虫的话,他就又会通过某些手段,对IP就进行限制甚至拉黑。像我故事中说的那样,一种解决方法就是自己添加区域代理。
所以,玩爬虫的时候还是有必要了解一些代理的知识的。
IP代理
先列举一些代理IP的网站,大家有需求的可以自取(为什么链接格式没有对齐,我不理解)
免费代理IP https://www.zdaye.com/Free/
免费代理IP http://ip.yqie.com/ipproxy.htm
66免费代理网 http://www.66ip.cn/
89免费代理 http://www.89ip.cn/
无忧代理 http://www.data5u.com/
云代理 http://www.ip3366.net/
快代理 https://www.kuaidaili.com/free/
极速专享代理 http://www.superfastip.com/
HTTP代理IP https://www.xicidaili.com/wt/
小舒代理 http://www.xsdaili.com
西拉免费代理IP http://www.xiladaili.com/
小幻HTTP代理 https://ip.ihuan.me/
全网代理IP http://www.goubanjia.com/
飞龙代理IP http://www.feilongip.com/
一般初学者,可能会用到以下方法去使用代理IP:
import requests
resp1 = requests.get('https://httpbin.org/ip').text
resp2 = requests.get('https://httpbin.org/ip', proxies={'http': 'xxx'}).text
为什么使用了代理以后,IP没有变呢?
这是因为你根本没有给https网站使用代理,你的代理只会对http网站生效。要对https网站生效,需要给它指定相应的https代理:
resp = requests.get('xxx', proxies={'http': 'xx:port', 'https': 'xx:port'}).text
这里贴出一段使用代理的模板:(没错,又是板子
import requests
proxies = {
"https": "https://58.209.234.8:3389",
"http": "http://10.10.1.10:3128",
"https": "https://10.10.1.10:1080",
}
resp = requests.get("https://www.baidu.com", proxies=proxies, verify=False)
resp.encoding = 'utf-8'
print(resp.text)
可能被识别的几种情况:
- 你的代理IP不是高匿代理
代理IP有三种常见的类型,透明代理,匿名代理和高匿代理。
使用透明代理的时候,网站可以同时看到代理IP和你的真实IP。用了等于白用。
使用匿名代理的时候,网站看不到你的真实IP,但是在请求里面有一个特征,可以告诉网站,你正在使用代理访问。
而只有真正的高匿代理,才能把你的爬虫请求隐藏起来。
有一些同学可能会从网上搜索免费的代理IP来使用。但这里面很多代理并不是高匿代理。肯定会被发现。
- 你的代理IP是服务器IP
有很多代理供应商,他们批量采购云服务器来搭建代理服务。例如国内的供应商会采购阿里云、腾讯云、华为云的服务器来搭建代理。海外的供应商会采购AWS或者Google云服务器。
如果你在云服务器上跑过不加代理的爬虫,你会发现,有时候一个爬虫,不加代理,在自己电脑上跑一点问题都没有,(啊对对对) 但是在云服务器上一跑就会被识别。这是因为云服务器的IP地址范围跟家用宽带是不一样的。
像AWS和Google云,他们的云服务器IP范围是公开的,只要网站提前把来自这个范围的所有请求全部禁掉,那么从AWS、Google云服务器上面发起的请求直接就会被当做爬虫请求。因此搭建在上面的代理服务自然就不会生效了。
国内的云服务供应商的服务器IP地址是否公布过我不太清楚,但他们的IP范围肯定是跟家用IP不一样的。网址遇到来自这些可疑IP范围的请求时,虽然不一定完全封禁,但是弹一个验证码出来测一测,还是可以挡住很多爬虫。
遇到这种情况,爬虫只有设法采购一些使用家用宽带搭建代理服务的供应商,才能解决问题。但这种代理价格肯定会翻好几倍。
- 服务器供应商的IP池被污染
有些人的爬虫写得非常垃圾,自以为有代理就无所畏惧,爬虫请求漏掉百出,网站即使不检查IP频率,也可以从其它特征知道这是爬虫请求,于是网站就会连带着这个代理IP一起封掉。而偏偏这种垃圾爬虫的请求速度又极快。哪怕代理供应商的IP池中有几百万个IP,也会很快被这些垃圾爬虫全部害死。
国内头部网站每天都会被数以千万计的爬虫请求访问,而主流的代理供应商就那么几家。如果很多垃圾爬虫都选中了同一家供应商,而他们的代理池更新又不及时。那么你使用了这家供应商的代理IP,自然一来就会被发现。
- 代理不支持HTTP/2
有一些网站需要使用HTTP/2请求。在Python里面,已经有不少HTTP客户端库支持发起HTTP/2的请求了,例如httpx。但是,现在很少有代理供应商能提供支持HTTP/2的代理,因为它搭建起来比较麻烦。于是,当你使用了支持HTTP/2的客户端,通过一个HTTP/1.1的代理IP访问一个HTTP/2的网站的时候,网站并不能正常返回内容。
上述资料来源: 为什么网站知道我的爬虫使用了代理? - 知乎 (zhihu.com),侵权立删。
User-Agent代理池
User-Agent即用户代理,简称UA,它是一个特殊 字符串头,使得服务器能够识别客户使用的 操作系统及版本、CPU类型、 浏览器及版本、 浏览器渲染引擎、浏览器语言、 浏览器插件等。【UA标准格式】:
浏览器标识 (操作系统标识; 加密等级标识; 浏览器语言) 渲染引擎标识 版本信息
举个例子:
user-agent:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xxx.0.0.0 Safari/xxx.36 Edg/xxx.0.1418.52
Mozilla/5.0
浏览器标识,(Windows NT 10.0; Win64; x64)
操作系统标识, AppleWebKit/537.36 (KHTML, like Gecko)
渲染引擎标识, Chrome/xxx.0.0.0 Safari/xxx.36 Edg/xxx.0.1418.52
浏览器版本
反爬更好的方式是使用User-Agent池来解决
(如生成随机User-Agent)
理解了UA是如何构成的,那么理解生成随机UA应该是不难的。
import random
def get_ua():
first_num = random.randint(55, 76)
third_num = random.randint(0, 3800)
fourth_num = random.randint(0, 140)
os_type = [
'(Windows NT 6.1; WOW64)', '(Windows NT 10.0; WOW64)', '(X11; Linux x86_64)',
'(Macintosh; Intel Mac OS X 10_14_5)'
]
chrome_version = 'Chrome/{}.0.{}.{}'.format(first_num, third_num, fourth_num)
ua = ' '.join(['Mozilla/5.0', random.choice(os_type), 'AppleWebKit/537.36',
'(KHTML, like Gecko)', chrome_version, 'Safari/537.36']
)
return ua
通过上述的自定义函数就可以得到随机UA了。
关于代理池,你还可以参考其他博客:
使用 MongoDB和flask构建一个代理池
报错
我们在学习的过程中总是会遇到各式各样的报错,然后各种爆红总是会让我们心烦意乱以及掉头发.
在这里整理了部分在爬虫学习的过程中可能出现的报错,仅供参考。
SSLError证书错误
requests.exceptions.SSLError: HTTPSConnectionPool(host='www.baidu.com', port=443)
原因
SSL证书报错
http连接太多没有关闭导致的。
经过一番查询,发现该错误是因为如下:
http的连接数超过最大限制,默认的情况下连接是Keep-alive的,所以这就导致了服务器保持了太多连接而不能再新建连接。
1、ip被封
2、程序请求速度过快。
解决方法
(1)time.sleep()
(2)关闭 SSL 验证 verify=False
response = requests.get(fpath_or_url,headers=headers,stream=True, verify=False)
(3) requests默认是keep-alive的,可能没有释放,加参数 headers={'Connection':'close'}
# TODO ssl证书报错,参数 verify=False,同时,requests默认是keep-alive的,可能没有释放,加参数
sess = requests.Session()
sess.mount('http://', HTTPAdapter(max_retries=3))
sess.mount('https://', HTTPAdapter(max_retries=3))
sess.keep_alive = False # 关闭多余连接
text = requests.get(self.target_img_url, headers=headers, stream=True, verify=False, timeout=(5,5)) # connect 和 read 二者的 timeout,所以是一个数组
with open(img_files_path, 'wb') as file:
for i in text.iter_content(1024 * 10):
file.write(i)
text.close() # 关闭,很重要,确保不要过多的链接
(4) 关闭系统代理(如果你开了ti 子的话
Event loop is closed
场景:协程创建时,会时不时报错RuntimeError: Event loop is closed
报错原因:
aiohttp 内部使用了 _ProactorBasePipeTransport ,程序退出释放内存时自动调用其 del 方法导致二次关闭事件循环。一般的协程程序是不会使用_ProactorBasePipeTransport 的,所以asyncio.run() 还是可以正常运行。而且这种情况仅在Windows上发生。
解决办法:
将asyncio.run(getCatalog(url))
改为
loop = asyncio.get_event_loop()
loop.run_until_complete(getCatalog(url)) # getCatalog(url)是协程主函数名
element click intercepted:
场景:在使用selenium模拟爬取信息时,可能想要点击页面中某个按钮,但是无法点击。
报错原因:大概是因为这个按钮上面还有别的东西覆盖
报错部分原代码:
web.find_element(By.XPATH,
'//*[@id="s_position_list"]/ul/li[1]/div[1]/div[1]/div[1]/a/h3').click()
改为:
h3 = web.find_element(By.XPATH,
'//*[@id="s_position_list"]/ul/li[1]/div[1]/div[1]/div[1]/a/h3')
web.execute_script("arguments[0].click();", h3)
'list' object has no attribute 'click'
可能是find_element_by_xpath
误用为 find_elements_by_xpath
decode()&encode()
python bytes和str两种类型可以通过函数encode()和decode()相互转换,
str→bytes:encode()
方法。str通过encode()方法可以转换为bytes。
bytes→str:decode()
方法。如果我们从网络或磁盘上读取了字节流,那么读到的数据就是bytes。要把bytes变为str,就需要用decode()方法。
Max retries exceeded with url...
原因:大概概率是被封IP了
解决方法:前面讲到的IP池