欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

网络爬虫设计中需要注意的几个问题

程序员文章站 2022-07-03 23:52:12
做网络爬虫是件很有意义的事情。首先,它可以是一个专门的职业。从公司层面讲,业务和战略可能都需要很多数据进行多维度分析,所以现在很多公司都有专门的爬虫工程师负责设计数据采集系统;其次,很多公司以爬虫为生,爬虫就是他们用来赚取利润的最主要手段,比如说各大搜索引擎和最近比较流行的即刻 APP;最后,爬虫也 ......

做网络爬虫是件很有意义的事情。首先,它可以是一个专门的职业。从公司层面讲,业务和战略可能都需要很多数据进行多维度分析,所以现在很多公司都有专门的爬虫工程师负责设计数据采集系统;其次,很多公司以爬虫为生,爬虫就是他们用来赚取利润的最主要手段,比如说各大搜索引擎和最近比较流行的即刻 APP;最后,爬虫也可以成为程序员业余时间赚取外快的好玩具,很多社群找程序员兼职爬取目标数据;最不济,它还可以成为一个好玩具,程序员可以抓取一些好玩的图片和文章,做一个自己喜爱的 Side Project。

我是通过看上的文章接触爬虫的。作者最近还写了本书「Python3网络爬虫开发实战 」,算是现在市面上比较系统的爬虫书籍了。我也写点东西总结一下做爬虫过程中遇到的主要问题,希望对没有接触过的同学有参考意义,也希望老鸟们帮忙看看路子是否正确。本文主要是为了厘清爬虫运行的思路,不会涉及太多的具体代码。

「网络爬虫」又叫网络蜘蛛,实际上就是一种自动化的网络机器人,代替了人工来获取网络上的信息。所以只要复原用户获取网络信息的步骤,就能够厘清爬虫运行的整个脉络。

网址管理

上网的时候,我先是输入一个网址:

网络爬虫设计中需要注意的几个问题

第一种策略称为「广度优先」,第二种策略称为「深度优先」。实际使用过程中一般是采用广度优先的策略。我们先从入口返回的数据中拿到我们感兴趣的 URL,放到一个列表中,每爬取完一个 URL,就把它放到已完成的列表中。对于异常的,另外作标记后续处理。

网络爬虫设计中需要注意的几个问题

实际上最简单的爬虫只作一件事:访问地址,获取数据。 当要访问的地址变得很多时,成立一个 URL 管理器,对所有需要处理的 URL 作标记。当逻辑不复杂的时候可以使用数组等数据结构,逻辑复杂的时候使用数据库进行存储。数据库记录有个好处是当程序意外挂掉以后,可以根据正在处理的 ID 号继续进行,而不需要重新开始,把之前已经处理过的 URL 再爬取一遍。以 Python3 为例,编写以下伪代码:

def main():
    root_url = 'https://www.cnblogs.com'
    res = get_content(root_url)
    first_floor_urls = get_wanted_urls(res)

    for url in first_floor_urls:
        res_url = get_content(url)
        if sth_wrong(res_url):
            put_to_error_list(url)
        else:
            sencond_floor_urls = get_wanted_urls(res_url)
    # rest of the code

if __name__ == '__main__':
    main()

什么语言可以做爬虫

虽然我会的语言不多,但是我相信任何语言,只要他具备访问网络的标准库,都可以很轻易的做到这一点。刚刚接触爬虫的时候,我总是纠结于用 Python 来做爬虫,现在想来大可不必,无论是 JAVA,PHP 还是其他更低级语言,都可以很方便的实现,静态语言可能更不容易出错,低级语言运行速度可能更快,Python 的优势在于库更丰富,框架更加成熟,但是对于新手来说,熟悉库和框架实际上也要花费不少时间。

比如我接触的 Scrapy,配环境就配了两天,对于里面复杂的结构更是云里雾里,后来我果断放弃了,任何爬虫我都只使用几个简单的库来实现,虽然耗费了很多时间,但是我对整个 HTTP 流程有了更深的理解。我认为:

在没有搞清楚设计优势的时候盲目的学习框架是阻碍技术进步的。

在我刚转行学习 Python 的那段时间,我每天都花很多时间在社区里去读那种比较 Flask,Django,Tornado 甚至是 Bottom,Sanic 这样的文章。这些文章很多都写得非常好,我也从中学到了很多知识,我知道了 Flask 胜在灵活,Django 更大更全面等等。

可是说真的,这浪费了我很多时间。新手总是有一种倾向,花费巨大的精力去寻找那些一劳永逸的方法,语言和框架,妄想只要学了这个,以后长时间就可以高枕无忧,面对各种挑战。如果要我重来一次,我会选择看一两篇这种优质的比较文章,然后大胆的选用其中一种主流的框架,在不重要的学习项目中尝试其他的框架,用了几次自然而然就会发现他们的优劣。

现在我还发现这种倾向不仅在新手中存在,老鸟也有很多患有这种技术焦虑症。他们看到媒体鼓吹 Go 语言和 Assembly,大家都在讨论微服务和 React Native,也不知所以的加入。但是有的人还是真心看懂了这些技术的优势,他们在合适的场景下进行试探性的尝试,然后步步为营,将这些新技术运用到了主要业务中,我真佩服这些人,他们不焦不燥热的引领着新技术,永远都不会被新技术推着走。

解析数据

本来应该叫解析网页,但是因为现在大多数数据都是在移动端,所以叫解析数据应该更合适。解析数据是说当我访问一个网址,服务器返回内容给了我,我怎么把我需要的数据提取出来。当服务器返回给我的是 HTML 时,我需要提取到具体哪个 DIV 下面的内容;当服务器返回给我的是 XML 时,我也需要提取某个标签下面的内容。

最原始的办法是使用「正则表达式」,这是门通用的技术,应该大多数语言都有类似的库吧,在 Python 中对应的是 re 模块,不过正则表达式非常难于理解,不到万不得已我真不想使用。Python 中的 BeautifulSoup 和 非常适合通过标签进行内容提取。

应对反爬虫策略

爬虫对于服务器是一种巨大的资源负荷,想象一下,你从云服务商那里买了个 30 块钱一个月的虚拟云服务器,搭建了一个小型的博客用于分享自己的技术文章。你的文章非常优质,很多人慕名来访问,于是服务器的响应速度变慢了。有些人开始做爬虫来访问你的博客,为了做到实施更新,这些爬虫每秒钟都要疯狂的访问几百次,这时候可能你的博客再也没人能成功获取到内容了。

这时候你就必须想办法遏制爬虫了。服务器遏制爬虫的策略有很多,每次 HTTP 请求都会带很多参数,服务器可以根据参数来判断这次请求是不是恶意爬虫。

网络爬虫设计中需要注意的几个问题

比如说 Cookie 值不对,Referer 和 User-Agent 不是服务器想要的值。这时候我们可以通过浏览器来实验,看哪些值是服务器能够接受的,然后在代码里修改请求头的各项参数伪装成正常的访问。

除了固定的请求头参数,服务器可能还会自定义一些参数验证访问是否合法,这种做法在 app 端尤其常见。服务器可能要求你利用时间戳等一系列参数生成一个 key 发送给服务器,服务器会校验这个 key 是否合法。这种情况需要研究 key 的生成,如果不行干脆用模拟浏览器以及虚拟机来完全冒充用户。

服务器还会限制 IP,限制 IP 的访问速度。比如我用 IP 为 45.46.87.89 的机器访问服务器,服务器一旦自认为我是爬虫,会立刻加入黑名单,下一次起我的访问就完全无效了。绝大多数的 IP 限制都不会有这么严格,但是限制访问速度是很常见的,比如服务器规定 1 个小时以内,每个 IP 只能访问 40 次。

这要求爬虫设计者要注意两件事:

  • 珍惜服务器资源,不要太暴力的获取服务器资源
  • 时刻注意 IP 代理池的设计

设计太快的访问速度是一种不道德的行为,不应该受到任何鼓励,服务器在受到爬虫暴力访问后可能会将迅速反应,将反爬虫策略设计得更加严格,因此我从来不将爬虫的速度设计得太快,有时候会延时 1 分钟再做下一次爬取,我始终认为免费获取别人的内容也应该珍惜。

在设计爬虫的时候不要忘记隐藏自己的真实 IP 来保护自己。IP 代理池是每一次访问都换不同的 IP,避免被服务器封掉。网上有很多免费的代理池,可以做个爬虫爬取下来存储备用。也有很多现成的库比如 就非常好用,安装完成以后访问本地地址就可以获取到可以用的 IP 列表。

爬虫和反爬虫会长时间斗志斗勇,除了上述问题还会遇到其他问题,比如说验证码设置。不同的验证码有不同的处理方式,常见的应对策略有买付费的验证服务,图像识别等。

其他具体的问题可以使用「抓包工具」去分析,比较常用的抓包工具有 charles 和 Fiddler,使用也很简单,搜教程看几分钟就会了。命令行我用过 mitmproxy,名字非常高大上,「中间人攻击」。我还尝试了 Wireshark,这个操作起来复杂得多,不过整个访问流程都不放过,不愧是学习 HTTP 的利器,有精力应该看一下 『网络是怎样链接的』和『WireShark网络分析就这么简单』这两本书,对理解网络访问非常有帮助。

抓包工具非常有用,不仅可以用来做爬虫分析,还可以用做网络攻防练习。我曾经用 Fiddler 发现了一个主流健身软件的很多漏洞,不过很快被他们发现了,他们通知我通过他们官方的渠道提交漏洞会有奖励,我照做了,不过没有得到他们的任何奖励和回复。可见,大公司也并不都靠谱。

模拟器

设计爬虫还需要注意一个非常残酷的现状:Web 端越来越 JS 化,手机端 key 值校验越来越复杂以致无法破解。这时候只能选择模拟器来完全假扮成用户了。

网页端常见的模拟浏览器工具有 Selenium,这是一个自动化测试工具,它可以控制浏览器作出点击,拖拉等动作,总之就是代替人来操作浏览器,通常搭配 PhantomJS 来使用。

PhantomJS 是一个基于WebKit的服务器端 JavaScript API,它基于 BSD开源协议发布。PhantomJS 无需浏览器的支持即可实现对 Web 的支持,且原生支持各种Web标准,如DOM 处理、JavaScript、CSS选择器、JSON、Canvas 和可缩放矢量图形SVG。不过目前好像已经停止维护啦。

不过没关系,Selenium 同样可以操作 FireFox 和 Chrome 等浏览器。如果有需要再学不迟。

除了 web 端,手机端 APP 也可以使用模拟器技术来完全模拟人的动作。我使用过 uiautomator,另外还有 Appium 非常强大,我暂时还没用过。

当需要并发的时候,我们手头上没有足够多的真机用来爬取,就要使用 genymotion 这样的虚拟机,使用起来跟 linux 虚拟机是一样的,下载安装包配置就可以了。

爬虫的并发和分布式

Python 作并发爬虫实际上毫无优势,不过如之前所讲,太高并发的爬虫对别人的服务器影响太大了,聪明的人不可能不作限制,所以高并发语言实际上优势也不大。Python 3.6 以后异步框架 Aiohttp 配合 async/await 语法也非常好用的,能在效率上提升不少。

至于分布式问题,我还没有好好研究,我做的大多数爬虫还达不到这个级别。我用过分布式存储,mongodb 配个集群不是很难。

总结

爬虫说起来是件简单的事情。但是往往简单的事情要做到极致就需要克服重重困难。要做好一个爬虫我能想到的主要事项有:

  • URL 的管理和调度。聪明的设计往往容错性很高,爬虫挂掉以后造成的损失会很小。
  • 数据解析。多学点正则表达式总是好事情,心里不慌。
  • 限制反爬虫策略。要求对 HTTP 有一定的理解,最好系统的学习一下。
  • 模拟器。这样做的效率有点低,而且电脑不能做其他事情。

我非常喜欢设计爬虫,以后我会尝试设计个通用性质的爬虫。这篇文章没有写具体的代码,因为我看到网上的源码都非常好懂,我就不做重复的事情了。我学爬虫的时候收集了几个,是 Python 的,如果你感兴趣,可以找我索要。

更多原创文章我会第一时间发布在公众号:wang_little_yong ,欢迎关注

网络爬虫设计中需要注意的几个问题