写爬虫的朋友肯定遇到过这种事。代码跑得正欢,突然弹出一堆错误。一看状态码,403或者429。IP被封了。那种感觉就像刚烧开的水被泼了一盆冷水。
我刚开始学爬虫的时候也这样。写了个简单脚本,抓某个电商网站的商品列表。一开始挺顺利,几百页数据唰唰地往数据库里灌。我还在想,爬虫也不过如此嘛。结果半小时后,一片红。全是被拒绝访问。我试着换了个网络,重新跑,五分钟又死。那个网站的反爬机制很敏感,同一个IP连续请求几十次就被拉黑了。
后来我想明白了,爬虫不能太莽。要学控制节奏。最直接的办法就是加延时。比如每抓完一页,用time.sleep(2)停两秒。如果网站速度慢,可以延长到5秒甚至10秒。这样对服务器友好,也不容易触发反爬。有些人图快,延时设成0.5秒,那跟直接攻击网站没区别。
但光靠延时不够。现在很多网站都用了动态IP检测。如果短时间大量请求来自同一个IP,还是会封。这时候就需要代理池了。
代理池就是一个可以轮换的IP集合。你可以从免费代理网站找,也可以付费购买高质量代理。我自己用的是一个付费代理池,每天几块钱成本,但稳定很多。每次请求前,从池里随机取一个IP,用requests库的proxies参数传进去。这样每次请求的IP都不同。就算某个IP被封了,换一个照样跑。
写代理池的时候要注意几点。第一,代理源要定期更新,免费代理经常失效。第二,要检查代理的可用性。拿到IP后先测一下能不能访问目标网站。第三,记录每个代理的成功率和响应时间。快的先选,慢的后选。
有个小技巧,我一般会同时准备几十个代理。跑之前先批量检测一遍,把可用的存下来。如果某个代理连续失败3次,就标记为“暂时禁用”,过一段时间再试。这样池子里的IP始终是活的。
限速也很关键。有些朋友觉得代理池能无限换IP,就拼命请求。这是大错。网站的反爬系统会看请求频率。就算IP换来换去,如果每秒请求量太大,一样会被识别出来。所以控制全局请求频率比换IP更重要。
我一般会用队列来控制。把所有要抓的链接放进一个队列,然后开几个子线程,每个线程从队列里拿任务。每个线程做完一个任务,就等几秒。这样整体速度稳定。如果你发现某个网站特别敏感,可以把线程数缩减到1个,单线程慢吞吞地抓。慢是慢了点,但不会封。
写爬虫像开长途车,不是要开多快,而是要开得稳。我有个朋友,接了个抓微博数据的任务。他用了一个付费的高匿名代理池,还故意把请求间隔设成30秒。结果跑了三天,稳稳的。最后数据量虽然不大,但全部拿到了。如果当初图快,可能第一天就翻车。
还有一点很多人忽略,就是请求头伪装。代理池换IP可以躲过IP封锁,但请求头也要跟着变。有些网站会检测User-Agent是不是一致。如果你固定用一个Mozilla标识,但不停的换IP,人家也起疑心。最好准备一个User-Agent列表,每次请求随机选一个。
举个例子吧,你一个浏览器不可能在Windows和Mac之间来回切换。所以User-Agent要跟OS一致。如果代理池里IP是物理机房的,User-Agent就用Linux或Windows的。如果是移动代理,可以用Android或iOS的。别搞混了。
有些朋友问,如果代理池里的IP都封光了怎么办?那就回到原点——加限速。先把代理池停掉,用本机IP跑几个请求,观察会不会封。如果本机IP不封,说明代理池本身质量不行。换一批代理源。如果本机IP也封,那说明网站的反爬太严,需要更复杂的策略,比如模拟登录、加cookies、甚至用Selenium模拟浏览器。
写爬虫不是写一次性脚本。你今天能跑通的代码,明天可能就不行了。网站的反爬技术也在升级。所以要留出调试时间。我一般会先做小规模测试,抓100条数据看看情况。确认没问题了,再开全量。
最后说个真实的事。我以前写一个爬小说网站的脚本,第一版只用了延时。跑了没多久就被封了。后来加了个免费代理池,结果代理质量太差,大部分都不通。折腾了一晚上,还是封。再后来,我买了付费代理,又把延时从1秒改成3秒,加上User-Agent随机切换。终于稳定了。那天晚上我抓了将近1万章小说,心里特别踏实。
爬虫被封是常事,谁还没被呼过几巴掌呢。关键是学会伺候IP、伺候服务器。别把自己当强盗,把自己当客人,礼貌一点,低调一点。数据自然会乖乖到手。