简介
我很久以前就想写一个高级爬虫教程了。这给我一个机会来展示scrapy的可扩展性,同时解决实践中出现的现实问题。尽管我很想做这件事,但是我还是无法摆脱这样一个事实:因为发布一些可能导致他人服务器由于大量的机器人流量受到损害的文章,就像是一个十足的坏蛋。
进群:125240963 即可获取数十套PDF哦!
只要遵循几个基本的规则,我就可以在爬取那些有反爬虫策略的网站的时候安心地睡个好觉。换句话说,我让我的请求频率和手动浏览的访问频率相当,并且我不会对数据做任何令人反感的事情。这样就使得运行爬虫收集数据基本上和以其他主要的手动收集数据的方法无法区分。但即使我遵守了这些规则,我仍感觉为人们实际想要爬取的网站写一个教程有很大的难度。
建立工程项目
添加一个基本的爬虫功能
现在我们需要添加一个Spieder类来让我们的scrapy真正地做一些事情。Spider类是scrapy爬虫用来解析文本,爬取新的url链接或是提取数据的一个类。我们非常依赖于默认Spider类的实现,以最大限度地减少我们必须要编写的代码量。这里要做的事情看起来有点自动化,但假如你看过文档,事情会变得更加简单。
在这个阶段,DOM检查器将起到很大的助力。如果你右击其中的一个页面链接,在DOM检查器里面查看它,然后你就会看到指向其他页面的链接看起来像是这样的:
我们的 parse(response) 方法现在能够返回字典类型的数据,并且根据它们的类型自动区分请求。每个字典都会被解释为一项,并且作为爬虫数据输出的一部分。
如果我们只是爬取大多数常见的网站,那我们已经完成了。我们只需要使用下面的命令来运行:
简单的问题
我们的第一个请求返回了一个 403 响应,所以这个url被爬虫忽略掉了,然后一切都关闭了,因为我们只给爬虫提供了一个 url 链接。同样的请求在网页浏览器里运行正常,即使是在没有会话(session)历史的隐匿模式也可以,所以这一定是由于两者请求头信息的差异造成的。我们可以使用 tcpdump 来比较这两个请求的头信息,但其实有个常见错误,所以我们应该首先检查: user agent 。
通过 AutoThrottle 扩展 ,上面的设置会创建一个稍微真实一点的浏览模式。我们的爬虫在默认情况下会遵守 robots.txt,所以现在我们的行为非常检点。
现在使用 scrapy crawl zipru -o torrents.jl 命令再次运行爬虫,应该会产生下面的输出:
下载中间件
在我们深入研究我们目前所面临的更复杂的问题之前,先了解一下请求和响应在爬虫中是怎样被处理的,将会很有帮助。当我们创建了我们基本的爬虫,我们生成了一个 scrapy.Request 对象,然后这些请求会以某种方法转化为与服务器的响应相对应的 scrapy.Response对象。这里的 “某种方法” 很大一部分是来自于下载中间件。
如果你对于有那么多的中间件默认是开启的感到惊讶的话,那么你可能有兴趣看看 体系架构概览。实际上同时还有很多其他的事情在进行,但是,再说一次,scrapy的最大优点之一就是你不需要知道它的大部分原理。你甚至不需要知道下载中间件的存在,却能写一个实用的爬虫,你不必知道其他部分就可以写一个实用的下载中间件。
困难的问题
看看第一个页面的源代码就会发现,有一些 javascript 代码负责构造一个特殊的重定向URL,并且构造浏览器的cookies。如果我们想要完成这个任务,那我们就必须同时解决上面这两个问题。
接下来,当然我们也需要解决验证码并提交答案。如果我们碰巧弄错了,那么我们有时会被重定向到另一个验证码页面,或者我们会在类似于下面的页面上结束访问:
请注意,这三个包都有 pip 无法处理的外部依赖,如果你运行出错,那么你可能需要访问 dryscrape, Pillow, 和 pytesseract 的安装教程,遵循平台的具体说明来解决。
我们的中间件现在应该能够替代原来的标准重定向中间件,现在我们只需要实现 bypass_thread_defense(url) 方法。我们可以解析 javascript 代码来得到我们需要的变量,然后用 python 重建逻辑,但这看起来很不牢靠,而且需要大量的工作。让我们采用更简单的方法,尽管可能还是比较笨重,使用无头的 webkit 实例。有几个不同选择,但我个人比较喜欢 dryscrape (我们已经在上面安装了)
现在让我们实现绕过威胁防御机制的基本逻辑。
这样就处理了我们在浏览器中遇到的所有不同的情况,并且完全符合人类在每种情况中的行为。在任何给定情况下采取的措施都取决于当前页面的情况,所以这种方法可以稍微优雅一点地处理各种不同的情况。最后一个难题是如果如何解决验证码。网上提供了 验证码识别 服务,你可以在必要时使用它的API,但是这次的这些验证码非常简单,我们只用 OCR 就可以解决它。使用 pytessertact 的 OCR 功能,最后我们可以添加 solve_captcha(img) 函数,这样就完善了 bypass_threat_defense() 函数。
很明显,scrapy 和 dryscrape 的请求头都绕过了最初的触发 403 响应的过滤器,因为我们现在不会得到任何 403 的响应。这肯定是因为它们的请求头信息不一致导致的。我的猜测是其中一个加密的访问cookies包含了整个请求头信息的散列值,如果这个散列不匹配,就会触发威胁防御机制。这样的目的可能是防止有人把浏览器的cookie复制到爬虫中去,但是它只是增加了你需要解决的问题而已。
现在,当我们可以通过命令 scrapy crawl zipru -o torrents.jl 再次运行爬虫。我们可以看到源源不断的爬取的内容,并且我们的 torrents.jl 文件记录把爬取的内容全部记录了下来。我们已经成功地绕过了所有的威胁防御机制。
总结
我们已经成功地写了一个能够解决四种截然不同的威胁防御机制的爬虫,这四种防御机制分别是:
User agent 过滤
模糊的 Javascript 重定向
验证码
请求头一致性检查
我们的目标网站 Zipru 可能是虚构的,但是这些机制都是你会在真实网站上遇到的真实的反爬虫技术。希望我们使用的方法对你自己爬虫中遇到的挑战有帮助。
网友评论