爬虫何时有用
假设我有一个服装店,并且想要及时了解竞争对手的价格。
总之,我们不能仅仅依赖于API去访问我们所需的在线数据,而是应该学习一些网络爬虫技术的相关知识。
本书基于Python 3
在本书中完全使用Python 3进行开发
对于初学者来说,我推荐使用Conda,因为其需要的安装工作更少一些。
编写第一个网络爬虫
抓取网站数据,我们首先得下载包含有感兴趣数据的网页,这个过程称之为爬取(crawling)。
一、抓取与爬取的对比
根据我们所关注的信息和站点内容、结构的不同,可能需要进行网络抓取或是网站爬取。
那么它们有什么区别呢?
例如:是否只用于抓取?是否也适用于爬虫?
二、下载网页
我们要想抓取网页的话,首先需要将其下载下来。示例脚本使用urllib模块下载URL。
传入URL参数时,该函数将会下载网页并返回其HTML。不过,这个代码片段存在一个问题,当我们下载网页时,可能会遇到一些无法控制的错误,比如请求的页面可能不存在。这个时候urllib会抛出异常,然后退出脚本。
安全起见,下面再给出一个更稳建的版本,可以捕获这些异常。
现在,当出现下载或URL错误时,该函数能够捕获到异常,然后返回None。
三、重试下载
我们在下载时遇到的错误一般都是临时性的,例如服务器过载时返回的503 Service Unavailable错误。
下面是支持重试下载功能的新版本代码:
现在,在download函数遇到5xx错误码时,会递归调用函数本身来重试。该函数还增加了一个参数,用于设定重试下载的次数,默认为两次。之所以在这里限制网页下载尝试次数,可能是服务器错误暂时还木有恢复。想要测这个该函数,可以尝试下载http://httpstat.us/500,这个网址会始终返回500错误码。
从返回的结果可以看出,download函数的行为和预期一致,先尝试下载网页,在接收到500错误后,又进行了两次重试才放弃。
四、设置用户代理
在默认情况下urllib使用Python-urllib/``3.x作为用户代理下载网页内容,3.x是正在使用的Python版本号。
即Web Scraping with Python的首字母缩写
现在再次访问meetup.com,就可以看到一个合法的HTML了。下载函数在后续代码中可以得到复用,这个函数能够捕获异常,在可能的情况下重试网站以及设置用户代理。
五、网站地图爬虫
在第一个简单的爬虫中,我们将使用示例网站robots.txt文件中发现的网站地图来下载所有网页。为了解析网站地图,就用一个简单的正则表达式,从标签中提取出URL。需要更新代码以处理编码转换,因为目前的download函数只是简单地返回了字节。
代码:
现在运行网站地图爬虫,从示例网站中下载所有国家或地区页面。
正如上面代码中的download方法所示,我们必须更新字符编码才能利用正则表达式处理网站响应。
下一节中,我们将会介绍另一个简单的爬虫,该爬虫不再依赖于Sitemap文件。
六、ID遍历爬虫
本节中利用网站结构的弱点,更加轻松地访问所有内容。
测试结果如下图所示
从上图可以看出,网页还是可以加载成功,也就是说这个方法是有用的。现在我们就可以忽略页面别名,只利用数据库ID来下载所有国家(或地区)的页面了。
来看看使用了该技巧的代码片段
我们现在可以使用该函数传入基础URL
在这段代码中对ID进行遍历,直到出现下载错误再停止,假设抓取已到达最后一个国家的页面。
但这种实现方式是有缺陷的,那就是某些记录可能已被删除,数据库ID之间并不是连续的。这个时候只要访问到某个间隔点,爬虫就会马上退出。以下是这段代码的改进版本,在这个版本中连续发生多次下载错误后才会退出程序。
以上代码中实现的爬虫得连续5次下载错误才会停止遍历,这样就极大地降低了遇到记录被删除或隐藏时过早停止遍历的风险。
七、链接爬虫
要运行这段代码,只需要调用link_crawler函数,并传入两个参数。要爬取的网站URL
用于匹配你想跟踪的链接的正则表达式
对于示例网站来说,我们想要爬取的是国家(或地区)列表索引页和国家(或地区)页面。
你会得到如下所示的下载错误:
可以看出,问题出在下载/index/1时,链接只有网页的路径部分没有协议和服务器部分,这是一个相对链接。
我们在运行这段代码时,虽然下载了匹配的网页,但是同样的地点会被不断重复下载到。
我们运行这个脚本的时候它会爬取所有地点,并且能够如期停止。最终得到了一个可用的链接爬虫。