使用Python爬虫,基于本地IP直接向网站发送请求以爬取数据时,可能由于网站的反爬措施而导致IP封禁。特别是在课堂演示等场景下,多台主机多次在同一IP发送请求,很难不出现这样的问题。这里以豆瓣电影Top250的爬取为例,对问题进行解析,并提供一种基于IP代理的解决方案。
问题描述
代码见:https://github.com/AWildFunny/IP-ProxyCrawler-for-doubanTop250/blob/main/web_crawler.py,为课堂上使用的演示代码,来源不详。
执行代码时,大多数同学的Python控制台出现“403 Forbidden”错误,说明请求不成功,豆瓣服务器没有正常返回一个网页。随后程序报错:
Traceback (most recent call last):
File "D:\Desktop\学校课程\数据分析方法与工具\web_crawler.py", line 156, in <module>
main()
File "D:\Desktop\学校课程\数据分析方法与工具\web_crawler.py", line 29, in main
saveData(datalist,savepath) #2种存储方式可以只选择一种
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\Desktop\学校课程\数据分析方法与工具\web_crawler.py", line 105, in saveData
data = datalist[i]
~~~~~~~~^^^
IndexError: list index out of range
这里的原因是:由于在askURL函数中request方法没有成功请求到网页,导致第39行代码返回的html为空,因此getData函数的返回值datalist为空,最终在saveData函数保存文件时代码第103行循环访问了空的datalist,导致列表越界(list index out of range)。
原因分析
根本原因在于:豆瓣网页设计了反爬虫逻辑,每个IP在多次请求(一般是50次左右)网页后就会遭封禁,一般48h左右解除。这时,访问豆瓣网页时会跳出如下的提示:
要解决这一问题有多个思路:
- 更换IP,比如从校园网切换为流量
- 若1.依旧不行,则可使用代理IP
- 使用模拟用户访问网页的爬虫方法(较复杂)
这里以思路2.为例,对代码进行修改,并演示使用代理IP的爬虫方法:
解决方案:基于IP代理的爬虫方法
首先代码进行以下几处修改:
- 修改askURL函数,使用代理IP发送请求,在main函数前设置代理IP的相关参数
- 在askURL函数中添加try/except以捕获错误
- 在getData函数的每次请求中添加随机延时,以防止IP被封
- 在getData函数中捕捉错误,在出现错误时再次尝试提交请求,即添加重试机制
- 修改了main函数中的savepath变量,删除保存位置目录中的result文件夹
修改后的完整代码见:https://github.com/AWildFunny/IP-ProxyCrawler-for-doubanTop250/blob/main/web_crawler_new.py
在使用时,首先需要获取一个代理服务器,这里以“蜻蜓代理”的免费试用服务为例:
- 首先访问网站注册账号,见https://proxy.horocn.com/
- 选择“隧道代理IP”进行免费试用(如下图),需要首先进行身份验证
- 开通成功后,在用户中心→隧道代理可以查看订单,如下:
将复制的订单号、密码分别填入程序中下面对应的部分(修改后的代码第24-28行):
# 添加代理服务器配置
proxyHost = "dyn.horocn.com"
proxyPort = "50000"
proxyUser = "你的订单号" # 替换为你的订单号
proxyPass = "你的密码" # 替换为你的密码
- 再次运行程序,此时会使用代理服务器发送请求,并不断尝试,直到完整爬取所有结果。若再次报错,可能是代理服务器的费用到期了。
讨论延伸
除豆瓣以外,如今的大部分网站,无论是社交媒体还是电商,都设置了大量的反爬取措施。在不使用“用户操作模拟”方法的前提下,在传统的爬虫方法上,要绕开反爬取逻辑就需要尽可能:
- 设置请求间隔,以免因为最简单的高频访问而被封禁;由此会降低爬取速度,因此同时采取多线程/异步爬取策略进行优化
- 优化请求头,添加用户登录的cookie等信息(不过仍然有账号被封禁的风险)
同时,这种方式依赖第三方的代理服务器,也就是有费用成本的。在尝试采用网上的免费代理IP时,几乎全部都超时无法使用。是否有更加简单的方法,能够避开类似网站的反爬虫逻辑呢?