因 7 中国主信出版集团多房哩明社 / 写 [澳] Richard Lawson 著 李斌 译 人民邮电出版社 北京 图书在版编目(CI P)数据 用Python写网络爬虫/(澳大利亚)理查德·劳森 (Richard Lawson) 著; 李斌译. 一北京:人民邮电出 版社, 2016.9 ISBN 978-7-115-43179一0 I. ①用…II . ①理…②李…III. ①软件工具一程 序设计N. ①TP311. 56 中国版本图书馆CIP数据核宇(2016)第177976号 版权声明 Cop严ight © 20 15 Packt Publishing. First published in the English language under the title Web Scraping with Python. All Rights Reserved. 本书由英国Packt Publishing公司授权人民邮电出版社出版。 未经出版者书面许可,对本书的任何部分不 得 以任何方式或任何手段复制和传播。 版权所有, 侵权必究。 ’ 著 [澳] Richard Lawson 译 李斌 责任编辑 傅道坤 责任印制 焦志炜 ’人民邮电出版社出版发行 北京市丰台区成寿寺路II号 邮编 100164 电子邮件 3 l 5@ptpress.com.cn 网址 http://www.ptpress.com.cn 三河市海波印务有限公司印刷 ’ 开本: 800x!OOO 1/16 印张: 10.75 字数z 148千字 印数z 1-3000册 著作权合同登记号 2016年9月第l版 2016年9月河北第1次印刷 图字:01-20 16-3962号 定价: 45.00元 读者服务热线: (010) 81055410 印装质量热线:(010) 81055316 反盗版热线:(010) 81055315 内容提要 本书讲解 了如何使用P川lOil来编写网络爬虫程序, 内容包括网络爬虫简 介, 从页面中抓取数据的三种方法, 提取缓存中的数据, 使用多个线程和进 程来进行并发抓取, 如何抓取动态页面中的内容, 与表单进行交互, 处理页 面中的验证码问题, 以及使用 Sca rpy和Portia 来进行数据抓取, 并在最后使 用本书介绍的数据抓取技术对几个真实的网站进行了抓取, 旨在帮助读者活 学活用书中介绍的技术。 本书适合有一定Python 编程经验, 而且对爬虫技术感兴趣的读者阅读 。 关于作者 Richard Lawson 来自澳大利亚 , 毕业于墨尔本大学计算机科学专业 。 毕 业后 , 他创办了一家专注于网络爬虫的公司,为超过 50个国家的业务提供远 程工作。 他精通于世界语 , 可以使用汉语和韩语对话,并且积极投身于开源 软件。 他目前在牛津大学攻读研究生学位,并利用业余时间研发自主无人机。 我要感谢Timothy Baldwin 教授将我引入这个令人兴奋的领域, 以及 本书编写时在巴黎招待我的ηiara 可 Douc 。 关于审稿人 Martin Bur咄是 一 名常驻纽 约的数据 记者 , 其工 作 是为华尔街日报绘 制 交互式图表。他在新墨西哥州立大学获得了新闻学和信息系统专业的学士学 位, 然后在纽约城市大学新闻学研究院获得了新闻学专业硕士学位。 我要感谢我的妻子Lisa鼓 励 我协助本书的创作 ,我的叔叔Michael 耐心解答我的编程问题, 以及我的父亲Richard激发了我对新闻学和写 作的热爱。 William Sankey是一位数据 专业人士,也是一位业余开发人员,生活在马 里兰州科利奇帕克市。 他于2012年毕业于约翰·霍普金斯大学, 获得了公共 政策硕士学位,专业方向为定量分析。他目前在L & M政策研究有限责任公司 担任健康服务研究员, 从事与 美国医疗保险和医疗补助服务中心 (C MS)相 关的项目。 这些项目包括责任医疗机构评估以及精神病院住院患者预付费系 统监测。 我要感谢我深爱的妻子Julia和顽皮的小猫Ruby,给予我全部的爱和 支持。 关于审稿人 Ayush Tiwari是一名Python开发者,本科就读于印度理工学院罗克分校。 他自2013年起工作于印度理工学院罗克分校信息管理小组,并活跃于网络开 发领域。 对他而言,审阅本书是一个非常棒的经历。他不仅 是一名审稿人, 也是一名狂热的网络爬虫学习者。他向所有Python爱好者推荐本书, 以便享 受爬虫的益处。 他热衷于Python网 络爬虫, 曾参与 体育直播订阅、通用P川lOil电子商务 网络爬虫(在Miranj)等相 关项目。 他还使用 Django 应用开发了就业门户,帮助改善印度理工学院罗克分校 的就业流程。 除了后端开发之外,他还喜欢使用诸如Nu mPy、 SciPy等Python库进行 科学计算和 数据分析,目前他从事计算流体力学领域的研究。你可以在GitHub 上访 问到他的项目,他的用户名是 ti waria yus h。 他喜欢徒步穿越喜马拉雅山谷,每年会参加多次徒步行走活动。 此外,他 还喜欢弹吉 他。 他的成就还包括参加国际知名的 Su per 30 小组, 并在其中成 为排名保持者。他在高中时, 还参加了国际奥林匹克数学竞赛。 我的家庭成员(我的姐姐Aditi、 我的父母以及Anand先生)、 我在 VI和IMG的朋友以及我的教授都为我提供了很大的帮助。我要感谢他们 所有人对我的 支持。 最后,感谢尊敬的作者和Packt出版社团队出版了这些非常好的技术 书籍。 我要对他们在编写这些书籍时的所有辛苦工作表示赞赏。 2 ..a.&. ....1』. 刷昌 互联网 包含了迄今为止最有用的数据集,并且大部分可以免费公开访问。 但是, 这些数据 难以复用。它们被嵌入在网 站的结构和样式当中, 需要抽取 出来才能使用。从网页中抽取数据的过程又被称为网络爬虫。随着越来越多 的信息被发布到网络上, 网络爬虫也变得越来越有用。 本书内容 第1章, 网络爬虫简介, 介绍了网络爬虫, 并讲解了爬取网站 的方法。 第2章, 数据抓 取,展示了如何从网页中抽取数据。 第3章, 下载缓存,学习了如何通过缓存结果避免重复下载的问题。 第4章, 并发下载, 通过并行下载加速数据抓取。 第5章, 动态内容,展示了如何从动态网 站中抽取数据。 第6章, 表单交互,展示了如何与 表单进行交互,从而访 问你需要的数据。 第7章, 验证码处理,阐述了如何访问被验证码图像保护的数据。 第8章, Scrapy,学习了如何使用流行的高级框架 Scrapy。 第9章,总结, 对我们介绍的这些网 络爬虫技术进行总结。 前言 阅读本书的前提 本书中所有的代码都己经在Python2.7 环境中进行过测试, 并且可以从 http: //bi tb uck et.org/wswp/code 下载到 这些 源 代码。 理 想情况 下, 本书未来的版本会将示例 代码移植到Python3 当中 。不过, 现在依赖的很多 库(比如 Sc ra py/Twi sted、 Mech anize 和Ghost ) 还 只支 持Python2 。为了帮 助 阐明爬取 示例 , 我 们创建 了一 个 示例 网 站, 其网址为 http: //exampl e. webscr api ng . co m 。 由于 该网 站限制 了 下载 内容的速度, 因此 如 果你 希 望 自行搭建示例网 站,可以从http ://bi tbuck et. org/wswp/places获取 网站源代码和安装说明。 我们决定为本书中使用的大部分示例搭建一个定制网站,而不是抓取活跃 网站, 这样我们就对环境拥有了完全控制。 这种方式为我们提供了稳定性, 因为活跃网 站要比书中的定制网 站更新更加频繁, 并且当你 尝试运行爬虫示 例时, 代码可能已经无法工作。 另外, 定制网 站允许我们自定义示例, 用于 阐释特定技巧并避免其他干扰。 最后, 活跃网 站可能并不欢迎我们使用它作 为学习网络爬虫的对象, 并且可能会尝试封禁我们的爬虫。 使用我们自己定 制的网 站可以规避这些风险, 不过在这些例子中学到的技巧确实也可以应用 到这些活跃网 站当中。 本书读者 阅读本书需要有一定的编程经验,并且不适用于绝对的初学者。在实践中, 我们将会首先实现我们自己的网络爬虫技术版本, 然后才会介绍现有的流行 模块, 这样可以让你更好地理解这些技术是如何工作的。 本书中的这些示例 将假 设你已经拥有Python语言以及 使用pip 安装模块的能力。如果你想复习 一下这些知识, 有一本非常好的免费在线书籍可以使用, 其作者为 Mark 2 前言 Pi lgr im,书籍网址是http: //www. diveint op ython. net 。这本书也是我 初学Python时所使用的资源。 此外,这些例子还假 设你己经了解网页是如何使用HTML 进行构建并通 过JavaScript 更新的知识。 关于HTT P、 css、AJAX、 WebK it 以及M ongoDB 的既有知识也很有用, 不过它们不是必需的,这些技术会在需要使用时进行 介绍。上述很多主 题的详细参考资料可以从 ht tp: //www. w3schools. com 获取到。 3 目录 第1章网络爬虫简介 1 .1 网络爬虫何时有用........... .... ........ 1. 2 网络爬虫是否合法 …… ……………… ………… ………………… …… …… ……… · 2 1.3 背景调研…………………………………………………………………………………··3 1.3.1 检查 robots.txt….......…...............................….............................3 1 . 3. 2 检 查网 站 地图 ......................... …......................…........................ 4 1.3.3 估算网 站 大小..........….......…………………………………………………5 1 . 3. 4 识 别网 站 所用技术 ………… …… ……… ……… ……… ………… …… …· 7 1 . 3.5 寻 找网 站所有者…. ......… ·· ……… ……… ………… ………… ………… ·· 7 1.4 编写第一个网络爬虫…..........................................….........…·················8 1.4.1 下载网页….........................…..........….........................................9 1.4.2 网站 地图 爬虫….........................…...................…······················12 1.4.3 ID遍历爬虫·············································································13 1.4.4 链接爬虫………………………………………………………………………··15 1.5 本章小结…··………………………………………………………………………………n 第2章数据抓取 23 2.1 分析网页…….................................….........................….....................…·23 2.2 三种网页抓取方法......................................…..................................…·26 2. 2. 1 正则 表达 式…………………… ……… ……… …… …… …… …… ……… …·26 目录 2.2.2 B eau tifu l Soup ·········…........................................……················28 2.2.3 Lxml·………………………………………………………………·················30 2.2.4 性 能对比.........................….........…................….........……·········32 2.2.5 结论.........……..............................................................……········35 2. 2.6 为链接 爬 虫添加 抓取 回调…........................ ......... ...................35 2.3 本章小结… .......… .......…………………………………………………………………38 第3章下载缓存 39 3.1 为链接爬虫添加缓存支持…·····················…·…....................................39 3.2 磁盘缓存...............................................................................................42 3.2.1 实现.........………………………………………………………………………·44 3.2.2 缓存测试..............……………………………………………………………46 3.2.3 节省磁盘空间…….......................….........…................……········ 46 3.2.4 清理过期数据…........................................................................47 3.2.5 缺点………………………………………………….......…........….........…· 48 3.3 数据库缓存...........................................................................................” 3.3.1 NoSQL是什么 ..............….......….......................……........…······50 3.3.2 安装M ongoDB …...................…….....................…····················50 3.3.3 MongoDB概述.......….........….................…...........................…50 3.3.4 MongoDB 缓存实现.................................…...............….......…52 3.3.5 压缩...............….........................................................................” 3.3.6 缓存测试…·”…....................…..........…........................…···········54 3.4 本章小结….........…............... ………………………………………………………妇 第4章并发下载 57 4.1 100万个网页.................…................................…..............…................57 4.2 串行爬虫.........................................…..................…................…........…60 4.3 多线程爬虫...................................…..................….........…...............…··60 2 目录 4.3.1 线程和进程如何工作...............................................................“ 4.3.2 实现........................................…........................…..................…61 4.3.3 多进程爬虫…............................................................................63 4.4 性能….........................…..........................….................….........……........67 4.5 本章小结 …··….......…......................….......…................….......................68 第5章动态内容 69 5.1 动态网页示例...............................................……..................................69 5.2 对动态网页进行逆向工程..............…·….........…............................……72 5.3 渲染动态网页.............................….......................….............................77 5 .3 .1 PyQ t 还是 PySid e……………… ……………………… …………………… 78 5 .3 .2 执行 J av aSc ript ···· ······ …. ...... …………………………………… ……….. 7g 5 .3 .3 使用W eb K it 与 网 站 交互 ....... …. ........ ....... …. ....................... … 80 5.3.4 Selenium ................................................................................... 85 5.4 本章小结.......…..........……………………………………………………...............gg 第6章表单交E 89 6.1 登录表单….........…....................................................….........................90 6. 2 支持内容更新的 登录脚 本扩展 …........ ........ ........ .. ….............. ........ ….. 97 6.3 使用M ec ha nize模块实现自动 化表单处理....... .. …....... …............ .... 100 6.4 本章小结 ………………………………………………………………………………·102 第7章验证码处理 103 7.1 注册账号…··……………………………………………………………………………·103 7.2 光学字符识别 ………… ………………………………………… …………………· … 106 7.3 处理复杂验证码….........…..........…................….......................…........ 111 7.3.1 使用验证码处理服务..............................…................….........112 7. 3. 2 9kw入门…………… …………………………………… ……… …………… 112 3 目录 7.3.3 与 注册功 能集成…..............……··············································119 7.4 本章小结 ..............…........…··………………………………………………………120 第 8章 Sc rapy 121 8.1 安装.............................…..................................……........................…··121 8.2 启动项目.........................…..........................................….......…··········122 8.2 . 1 定 义模型.........… ………………………………………………… ………… 12 3 8. 2. 2 创建爬 虫........……… …………………………………………………······· 12 4 8. 2. 3 使用shell命令 抓 取 …...........… …………………………… …………· 12 8 8.2.4 检查结果..............….......…………………………………………………129 8.2.5 中断与恢复爬虫”…...............……..............…··························132 8.3 使用P ort ia 编写可视化爬虫 ................. ............……··· ························ 13 3 8.3.1 安装…..............................….............…·····································133 8.3.2 标注……………………………………………………………………………··136 8. 3. 3 优化爬 虫…··……………………………………… ……… …………………· 13 8 8.3.4 检查结果…......................………………………………………………··140 8.4 使用 Scra pely实现自动 化抓取.........… .......... ................ ........ ........… 14 1 8.5 本章小结…........................…..........…........…......................…··············142 第9章 总结 14 3 句300OOAU’I句3 휌, AιTA丛TA件,、dp气d F飞d,、d 4·EA唱’EA唱BEA--EA唱-EA --EA唱··A 擎23· 索…站千…搜业网M… … 结 km M Hu h- 句J ,,町 鸣叫UUP马章AUh吭吭AU宝本咱,“ 咱3AaT ZJ 0707 070707 4 本章中, 我们将会介绍如下主题: · 网络爬虫领域简介: · 解释合法性质疑: · 对目标网 站进行背景调研1 ·逐步完善一个高级网 络爬虫。 1.1 网络爬虫何时有用 第1章 网络爬虫简介 假设我有一个鞋店, 并且想要及时了解竞争对手的价格。 我可以每天 访问他们的网 站 , 与我店铺中鞋子的价格进行对比。但是, 如果我店铺中 的鞋类品种繁多,或是希望能够更加频繁地查看价格变化的话, 就需要花 费大量的时间,甚至难以实现。再举一个例 子, 我看中了一双鞋, 想等它 促销时再购买。我可能需要每天访问这家鞋店的网 站 来查看这双鞋是否降 价, 也许需要等待几个月的时间, 我才能如愿盼到这双鞋促销。 上述这 两个重复性的手工流程,都可以利用本书介绍的网络爬虫技术实现自动化 处理。 第 1 章 网络爬虫简介 理想状态下, 网络爬虫并不是必须品,每个网 站 都应该提供API, 以结构 化的格式共享它们的数据。 然而现实情况中, 虽然一些网 站 已经提供了这种 API,但是它们通常会限制可以抓取的数据,以及访问这些数据的频率。另外, 对于网 站 的开发者而言,维护前端界面比维护后端AP I接口优先级更高。总 之, 我们不能仅仅 依赖于API 去访 问我们所需的在线数据, 而是应该学习一 些网 络爬虫技术的相 关知识。 1.2 网络爬虫是否合法 网络爬虫目前还处于早期的蛮荒阶段,“允许哪些行为” 这种基本秩序还 处于建设之中。 从目前的实践来看, 如果抓取数据的行为用于个人使用, 则 不存在问题:而如果数据用于转载, 那么 抓取的数据类型就非常关键了。 世界各地法院的一些案件 可以帮助我们确定哪些网 络爬虫行为是允许 的。 在Feist Publications, Inc. 起诉Rural Tel写phone Service Co. 的案件中, 美 国联邦最高法院裁定抓 取并转载真实数据 (比如,电话清 单〉是允许的。 而 在澳大利亚, Telstra Corporation Limited 起诉Phone Directories Company Pty Ltd 这一类 似案件中, 则裁定只有拥有明确作者的数据, 才可以获得版权。 此外, 在欧盟的ofir.dk起诉home.dk一案中, 最终裁定定期抓取和深度链接 是允许的。 这些案件 告诉我们,当抓 取的数据是现实生活中的真实数据(比如,营业 地址、 电话清 单) 时, 是允许转载的。 但是, 如果是原创数据 (比如,意见 和评论), 通常就会受到版权限制, 而不能转载。 无论如何,当你抓 取某个网站 的数据时,请记住自己是该网站 的访 客,应 当约束自己的抓 取行为, 否则他们可能会封禁你的 IP ,甚至采取更进一步的 法律行动。 这就要求下载请求的速度需要限定在一个合理值之内, 并且还需 要设定一个专属的用户代理来标识自己。 在下面的小节中我们将会对 这些实 践进行具体介绍。 2 1.3 背景调 研 关于上述几个法律案件的更多信息可以参考下述地址: • http://caselaw.lp.findlaw.com/scripts/ getcase. pl?court=US&vol=499&invol=340 £· http://www.austlii.edu.au/au/cases/cth/ FCA/2010/44.html • http://www.bvhd.dk/uploads/tx mocarticles /S og Handelsrettens afg relse i Ofir-sagen.pdf 1.3 背景调研 在深入讨论爬取一个网 站 之前,我们首先需要对目标站点的规模和结构进 行一定程度的了解。 网站 自身的robots.txt 和Sit emap 文件都可以为我 们提供一定的帮助,此外还有一些能提供更详细信息的外部工 具,比如Google 搜索 和 WHO IS。 1.3.1 检查robots.txt 大多数网站 都会定义robots.txt文件, 这样可以让爬虫了解爬取该网站 时存在哪些限制。 这些限制虽然仅仅 作为建议给出,但是良好的网络公民都应 当遵守 这些限制。 在爬取之前, 检查robots.txt文件这一宝贵资源可以最小 化爬虫被 封禁的可能,而且还能 发现 和网 站结构相关的线 索 。关于 robot s. tx t 协议的更多信息可以参见 ht tp ://www. robotstxt.org 。 下面的代码是我 们的示 例文件 robots.txt 中的内容, 可以访问http ://examp le. webscraping. co m/robots. txt 获取。 r e -- w a r 户Ud a B 叮4· ·㌏ 卡M nn ·· oew -- qo ta 1le c- -4 era ses s· 工 #UD 3 第 1 章 网络爬虫简介 # section 2 User-agent : * Crawl-delay: 5 Disallow : /trap # section 3 Sitemap : http://example.webscraping. com/sitemap .xml 在sect ion 1 中, robots. txt 文件禁止用户代理为BadCrawler 的 爬虫爬取该网站 , 不过这种写法可能无法起 到应有的作用, 因为恶意爬虫根 本不会遵从robots. txt 的要 求。本章后面的一个例子将会展 示如何让爬虫 自动遵守robots. txt 的要 求。 sect ion 2规定,无论使用哪种用户代理, 都应该在两次下载请求之 间给出5秒的抓取延迟, 我们需要遵从该建议以避免服务器过载。 这里还有 一个/trap 链接,用于封禁那些爬取了不允许链接的恶意爬虫。如果你访问 了这个链接, 服务器就会封禁你的 IP 一分钟! 一个真实的网站可能会对你 的 IP 封禁更长时间,甚至是 永久封禁。 不过如果这样设置 的话, 我们就无 法继续这个例子了。 sect ion 3定义了一个Si temap 文件, 我们将在下一节中了解如何检 查该文件。 1.3.2 检查网 站地图 网站提供的Sitemap 文件(即网站 地图) 可以帮助爬虫定位网站 最新的 内容, 而 无须爬取每 一个网页。 如 果 想要了解更多信 息 , 可 以从 http: //www. sitemaps. or g/protocol.html 获取网 站 地图标准的定 义。 下面是 在robots. txt 文件中发现的Sitemap 文件的内容。 http ://example . webscraping. com/view/Afghanistan-1 http ://example . webscraping. com/view/Aland-Islands-2 4 1.3 背景调研 http ://example . webscraping. com/view/Albania-3 网站地图提供了所有网页的链接, 我们会在后面的小 节中使用这些信息, 用于创建我们的第一个爬虫。 虽然Sitemap文件提供了一种爬取网站的有效 方式, 但是我们仍需对其谨慎处理, 因为该文件经常存在缺失、 过期或 不完 整的问题。 1.3.3 估算网站大小 目标网站的大小会影响我们如何进行爬取。 如果是像我们的示例站点这样 只有几百个URL的网站 , 效率并没有那么 重要:但如果是拥有 数百万个网页 的站 点, 使用串行下载可能需要持续数月才能完成, 这时就需要使用第 4 章 中介绍的分布式下载来解决了。 估算网站 大小 的 一个简便方法是检查G oo gle爬 虫的 结果,因 为G oo gle 很 可 能已经爬 取 过我们感兴趣的网 站 。 我们可以通 过Goo gl e 搜 索的site 关 键 词 过 滤域名 结果, 从而获 取 该 信 息 。 我 们可以从 http://www. google. com/ad va口ced search 了解到该 接口 及其他高 级搜 索参数的用法。 图 1.1 所示为使用site 关键词对我们的示例网 站进行搜索的结果, 即在 Google中搜索 site: ex am ple. webscra pin g. com o 从图 1.1 中可以看出,此时Google估算该网站拥有 202 个网 页, 这和实 际情况差不多。 不过对于更大型的网站, 我们 会发现Google 的估算并 不十 分准确。 在域名后面添加URL 路径, 可以对结果进行过滤, 仅显示网站的某些部 分。 图 1.2 所示为搜索 site: exam ple. webscra ping. com/ view 的结果。 该搜索 条件会限制Google只搜索国家页面。 5 第 1 章 网络爬虫简介 Aboul202re田JltS伺45蕃emnds) AF • Example web scrap�ng website 辄酬阳.we民町,商ping.oomlcor世in回I/AF• Exam阱嗣b쿩ing \¥盹脚.Arri目,闻geria · Angela · senin · Sotswana ·刷刷na Fa回·6田undl C副陌roon ca阳Verde ·C田11ra1 Arri国n R邱刷lc·Ch副. NA-εxa『nple web scraP.ing webs挂e ex翻F瑞e E翼샃pie web缸万冒pir咱w雹』画捶. IJ。曲Arr咀e拖a An窜山l国 Anti凯iaar晴B酣buda A阳ba Sahaπ、a&·Sa『1胁ad。S•B毫撼IZI>”Bl>伺11Ud握’哥。nal「·a, Saint Eus恒屈JS and . NE-E姐mple �eb scraping website e阻mpl且W创JSCI'胃病’g.oomllso/捋E• Nalklnal Fl咽’M回:1,267,000 S블惜먳阳elli剧,问pula如n: 10,676,27专lso: NE. Country: IJ胁,.Cai就醋’Nlam町C曲睛酣吐AF. Tlcl: J班Curr田,cy Code: XOF. NG - Example web sαaping website e刷脚.wet刷刷ng.comlisol闸, National Fl啕:滽:923,768 squa阳闹。metres. Pcψul硝or应154,00口,000.1聪:NG. C咀Jntry: N精制a巳apl国E Alli!由a con剧团提:AF. Tld: .ng. currency C时居:NGN. 图1.1 About 117明ults (0.52 seam峭 Example web部raping website 就ami归”W确scraping.α:m1/view/Guemse·沪倪, Natio捕’F陆g:A,回:76squ町e kllom钝国. Population: 65,22苗,i四:GG. Cou晴y. G佳emse于“Capital: St Peter pॹCon伽ent EU. Tld: .gg. Currency Code: GBP. Example web scraping website 回am树e.w唾民臼撞倒ng.comfv梅w/Je陀曹y-113 ... Nat阳nal F句:阳明:116呵U脚扭llornet阳.P句“la;阂,612. lso: JE. Country: Jerse沪 Capital: Saini Hel田.Conti阴阳t EU.T陆:.je. Currency Code: GBP. PK - Example �eb scraping website e泪m阱.webscra酬g.comlv恒w/P副stan-1邸, National Fl句:A陌a:8部,S铅squa用kffome悦s. Population: 184,404,791.国:PK. C由mtry: Pak陆;tan. Cap幅:1时ama缸ad.Cont加剧1l:AS“lld: .pk Currency Code ... Example web scrapinQ website - WebScraping.com example.w盼scra阪ng.com/view/Ma阳䭧134 ... Na悔自at Flag; Area: 329,750 squa阳kllometJ髓,Popi!翩。n: 28,27 4, 729精I盹:MY“ C皿miry: Ma姐ysia. Ca竽:Kuala L四npur. Con11nent AS. Tld: .my. C町rency町 图1.2 这种 附加的过滤条件非常有用,因为在理想情况下,你只希望爬取网站 中 包含有用数据的部分,而不是爬取网站 的每个页面。 6 1.3 背景调研 1.3.4 识别网 站所用技术 构建网站所使用的技术类型也会对我们如何爬取产生影响。 有一个十 分有 用的工具可以检查网站 构建的技术类型̍builtwith 模块。 该模块的安装 方法如下。 pip install builtwith 该模块将URL 作为参数,下载该URL并对其进行分析,然后返回该网站 使用的技术。 下面是使用该模块的一个例子。 》> imp。rt builtwith 》> builtwith .parse (’http://example .webscraping .c。m’} {u’j avascript-framew,。rks ’:[u ’ jQl且ery ’ , u’M。dernizr’ , u’jQl且ery UI ’ ] I u’pr。gramming-languages’ : [u ’Pyth。n ’ ] I u’web-framew。rks ’: [u ’ Web2py’ , u’Twitter B。。tstrap ’ ] , u ’web-servers ’: [u ’Nginx ’ ]} 从上 面 的返回 结果中可以看 出 , 示 例网 站使用了 Pyth on的Web2 py 框架 , 另外还使用了 一些通用的JavaScript 库,因此该网站的内容很有可能是嵌入 在 HTML中的 , 相 对而言比 较容易抓取。 而如果改用 A ng ula rJS构建该网 站 ,此 时的网 站 内容就很 可 能是动 态 加载的 。 另外, 如果网站 使用了 ASP. NE T , 那 么 在爬取网页时, 就必须要用到会话管理和表单提交了。 对于这些更加复杂 的情况, 我们会在第5 章和第6 章中进行介绍。 1.3.5 寻找网站所有者 对于一些网站 , 我们可能会关心其所有者是谁。 比如, 我们已知网站的所 有者会封禁网 络爬虫, 那么我们最好把下载速度控制得更加保守一些。 为了 找到网 站的所有者,我们可以使用WHOIS协议查询域名的注册者是谁。Python 中有一个针对该协议的封 装库, 其文档地址为 https: // pypi. python. or g/ py pi/ python-whois , 我们可以通过 pi p进行安装。 7 第l章 网络爬虫简介 pip install pyth。n-wh。is 下面是使用该模块对 appspot.com 这个域名进行WHOIS 查询时的返回 结果。 》> imp。rt wh。is 》> print whois.wh。is (’appsp。t.com’} 、四.e_servers" : [ ] , ”NSl .G。。GLE .COM” , ”NS2 .G。。GLE .COM” , ”NS3 .G。。GLE . COM” , ”NS4 .GࠂGLE . C。M” , ”ns4 . q。。qle .com”, ”ns2 . q。。qle .com” , ”nsl . q。。qle .com” , ”ns3 . q。。qle . c。m” ”。rq” : ”G。。qle Inc.”, ”e啤ails”: [ ”abusecomplaints@markm。nit。r . com” , ”dns-ad皿in@q。。gle .com” 从结果中可以看出该域名归属于 Google, 实际上也确实如此。 该域名是 用于GoogleApp En g ine服务的 。 当 我们爬 取 该域名时就需要十 分小 心 , 因 为 Google经常会阻断网络爬虫, 尽管实际上其自身就是一个网络爬虫业务。 1.4 编写第一个网络爬虫 为了抓 取网站 ,我们首先需要下载包含有感兴趣数据的网页,该过程一般 被称为爬取(crawling)。 爬取 一个网站有很多种方法, 而选用哪种方法更加 合适, 则取决于目标网站的结构。 本章中, 首先会探讨如何安全地下载网页, 然后会介绍如下3 种爬取网站的常见方法: 8 · 爬取网站地图1 · 遍历每个网页的数据库ID; · 跟踪网页链接。 1.4.1 下载网页 1.4 编写第一个网络爬虫 要想爬取网页,我们首先需要将其下载下来。下面的示例脚本使用Py thon 的ur llib 2 模块下载URL。 import urllib2 def download (url) : return urllib2 .url。pen (url) . read () 当传入URL参数时, 该函数将会下载网页并返回其HTML。 不过, 这个 代码 片段存在一个问题, 即当下载网页时, 我们可能会遇到一些无法控制的 错误, 比如请求的页面可能不存 在。 此时,ur lli b2 会抛出异常,然后退出 脚本。 安全起 见, 下面再给出一个更健壮的版 本, 可以捕获这些异常。 import urllib2 def download (url) : print ’ D。wnl。ading : ’, url try : html = urllib2 . urlopen (url) . read () except urllib2 .URLError as e: print ’Downl。ad err。r: ’, e.reas。n html = None return html 现在, 当出现下载错误时, 该函数能够捕获到异常, 然后返回No ne。 1. 重试下载 下载时遇到的错误经常是临时性的, 比如服务器 过载时返回的 503 Ser vic e Un availab le 错误 。 对 于此 类错 误, 我们可以 尝 试重 新 下载, 因 为这个服务器 问题现 在可能己解决。 不过, 我们不需要对所有错误都尝试重 9 第1章 网络爬虫简介 新下载。 如果服务器返回的是404 Not Found 这种错误, 则说明该网页目 前并不存 在, 再次尝试 同样的请求一般 也不会出现不同的 结果。 互联网工程任务组(Internet En gineeri ng Tas k Forc e)定义了Hπ? 错误的 完 整列表, 详情可参考https: //tools. ie tf. or g/h tml/rfc 72 31# section - 6。从该文档中,我们可以了解到4xx 错误发生在请求存在问题时, 而5xx 错误则发生在服务端存在问题时。 所以, 我们只需要确保download 函数在 发生Sxx 错误时重试下载即可。 下面是支持重 试下载功能的新版 本 代码。 def download (url, num retries=2): print ’ D。wnloading: ’, url try: html = urllib2 . urlopen (url) .read () except urllib2 .URLError as e: print ’Download error: ’, e.reason html = None if num retries > 0: if hasattr (e, ’code ’) and 500 <= e.code < 600: # recursively retry Sxx HTTP errors return download (url, num retries-1) return html 现在, 当 download函数遇到Sxx 错误码时, 将会递归调用函数自身进 行重试。 此外, 该函数还增加了一个参数, 用于设定重试下载的次数, 其默认 值为两次。 我们在这里限制 网页下载的尝试次数, 是因为服务器错误可能 暂时 还没有解决。想要测试该函数, 可以尝试下载http: //httpstat. us/500, 该网址会始终 返回500 错误码。 》> d。wnl。ad (’http : //httpstat.us/500 ’} Downl。ading : http://httpstat.us/500 D。wnload err。r : Internal Server Err。r D。wnl。ading : http://httpstat.us/500 Download err。r : Internal Server Err。z D。wnl。ading : http://httpstat .us/500 D。wnload err。r : Internal Server Err。r 10 1.4 编写第一个网络爬虫 从上面的返回结果可以看出, down load 函数的行为和预期一致, 先尝 试下载网 页, 在接收到500 错误后, 又进行了两次重试才放弃。 2. 设置用户代理 默认情况下, ur llib2 使用 Python-ur llib/ 2. 7 作为用户代理下载 网 页内容, 其中 2. 7是 Python 的版 本号。 如果能使用可辨识的用户代理则 更好, 这样可以避免我们的网络爬虫碰到一些问题。 此外, 也许是因为曾经 历过质量不佳的Python网 络爬虫造成的服务器 过载,一些网 站 还会封禁这个 默认的用户代理 。 比如, 在使用 P同lOil默认用户代理 的情况下,访问 http://www.meetup.com /, 目 前会返回如图 1.3 所示的访问拒绝提示。 Access denied η1e owner of this website (www.meetup.com) has banned your access based on your browser's signature ( 175413467,仿Rlae4-ua48). • Ray ID: 1754l34676eft>ae4 • Tim臼饱mp: Mon.06心ct-14 18:55:48 GMT • Yo咀r IP address: 83.27.128.162 • Reques撞ed URL: www.mee灿p.com/• E.π·or reference number: I 0 IO • Server ID: FL 33F7 • User-Agent:时也on-urllib/2.7 图1.3 因此,为了下载更加可靠, 我们需要控制用户代理的设定。 下面的代码对 download函数进行了修改, 设定了一个默认的用户代理“wsw p”( 即Web Scraping with Python 的首字母缩写)。 def d。wnload (url, user agent= ’wswp’, num retries=2) : print ’ Downloading: ’, url headers = {’User-agent ’: user agent } request = urllib2 . Request (url, headers=headers) try: html = urllib2 . urlopen (request) • read () except urllib2 . URLError as e: print ’Download error: ’, e.reason html = None 11 第l章 网络爬虫简介 if num retries > 0: if hasattr (e, ’code ’) and 500 <= e.code < 600: # retry 5XX HTTP errors return downl。ad (url, user agent, num retries-1) return html 现在 ,我们拥有了一个灵活的下载函数, 可以在 后续示例中得到复用。 该 函数能够捕获异常、 重试下载并设置 用户代理。 1.4.2 网站地图爬虫 在 第一个简单的爬虫中, 我们将使用示例网站 robots. tx t文件中发现 的网站地图来下载所有网页。 为了解析网站 地图, 我们将会使用一个简单的 正则表达式, 从< loc >标签中提取出URL。 而在 下一章中, 我们将会介绍一 种更加健壮的解析方法̍css选择器。 下面是该示例爬虫的代码。 def crawl sitemap (url) : # download the sitemap file sitemap = downl。ad (url) # extract the sitemap links links = re . findall (’ <loc> ( . *?) ]+href= [”\ ’] (.丰?) [”\ ’]’, re. IGNORECASE) # list of all links from the webpage retur口 webpage regex.findall(html) 要运行这段代码, 只需要调 用 link crawler函数, 并传入两个参数: 要爬取的网站 URL和用于跟踪链接的正则表达式。对于示例网站 , 我们想要 爬取的是国家列表索 引页和国家页面。 其中, 索 引页链接格式如下。 • ht tp: I I ex ampl e. webs crap in g. com/ index/ 1 • ht tp: I I ex ample. webs crap ing. com/土口dex/2 国家页链接格式如下。 • ht tp: I /examp le. webscrap ing. com/ view/A fghanist an-1 • ht tp: I /examp le. webs crap ing. com/ view/A land-I sla口ds- 2 因此, 我们可以用/(index[ view)/这个简单的正则表达式来匹配这两 类网页。 当爬虫使用这些输入参数运行时会发生什么呢?你会发现我们得到 了 如下的下载错误。 》> link crawler (’http:/ /example.webscraping.com’, ’I (index I view) ’) Downloading: http://example.webscraping.com Downloading: /index/1 Traceback (most recent call last) : ValueError: unknown url type : Ii口dex/1 16 1. 4 编写第一个 网络爬虫 可以看出, 问题出在下载/index/1时, 该链接只有网页的路径部分, 而没有协议和服务器部分, 也就是说这是一个相对链接。 由于浏览器知道你 正在浏览哪个网页, 所以在浏览器浏览时, 相对链接是能够正常工作的。 但 是 , ur lli b 2是无法获 知 上下文 的。为了让ur llib 2 能 够定位 网页, 我们 需要将链接转换为绝对链接的形式, 以便包含定位网页的所有细节。 如你所 愿,Py thon中确实有用来实现这 一功能的模块, 该模块称为urlparse。 下 面 是li nk crawl er 的改进版本, 使用了 url parse 模 块来 创 建绝对 路 径。 import urlparse def link_crawler (seed url, link regex) : ”””Crawl from the given seed URL following links matched by link regex ) ( P 1』O lp r- ue -u de e- -u e eqA SU - rL e㜍 uw =q品 a-r e14 C UW ea= ur q企cl -r leu wl aE 工 rh cw html = download (url) for link in get links (html) : if re .match (link_regex, link) : li口k = urlparse.urljoin ( seed url, link) crawl_queue .append (link) 当你 运行这段 代码时,会发现虽然网页下载 没有出现错误,但 是同样 的地 点总是会被不断下载到。 这是因为这些地点相互之间存在链接。比如, 澳大 利 亚链接到了南极洲, 而南极洲也存在到 澳 大利亚的 链接 , 此 时爬虫 就会在 它们之间 不断循环 下去。 要想避免 重复 爬 取相 同 的 链接 , 我们 需 要记录哪些 链接己经被 爬 取过。 下 面是修改后的 li nk craw ler函 数, 己具备存储己 发 现URL的功能,可以避免重复下载。 def link crawler (seed url, link regex) : crawl queue = [ seed url] # keep track which URL’s have seen before ) 14 m ←L )h (( ps 01’ 咱K )p l口 e- ZE 工 ue u14 eu ( - ue dt q· ·uae -e q4o qJ lu -- we 14nn auwW E工 zq- ao c- zdk f、 14c n tw =E l ea= 14 sr 14 cl mr =r to eu hf 口14 e- l eh sw 17 第l章 网络爬虫简介 # check if link matches expected regex if re . match(link regex, link) : # form absolute link link = urlparse. urljoin(seed_url, link) # check if have already seen this link if link not i口 seen: seen. add(link) crawl queue . append(link) 当运行该脚本时,它会爬取所有地点, 并且能够如 期停止。 最终, 我们得 到了一 个可用的爬虫! 高级功能 现在,让 我们为链接爬虫添加一些功能,使其在爬取其他网站 时更加有用。 解析robo ts.t xt 首先, 我们需要解析robots. txt 文件, 以避免下载禁止爬取的URL。 使用Python自带 的robotparser 模块, 就可以轻松完成这项工作, 如下面 的代码所示。 》> imp。rt z。b。tparser 》> rp = r。b。tparser . Rob。tFileParser () 》> rp . set_url ('http : //ex四1ple . webscraping . com/rob。ts .txt ’) 》> rp .read() 》> url = ’http://example . webscraping .com ’ 》> user_agent = ’BadCrawler ’ 》> rp . can_fetch (use�二agent , url) False 》> user_agent = ’G。。dCrawler ’ 》> rp . can_fetch (user_agent, url) True robotparser 模块首先加载robots.txt 文件,然后通过can_fetch () 函 数 确 定指定的 用户代理是否允许访 问网 页 。 在本 例 中 , 当用户代理设 置 为 ’B adCraw ler ’时 ,robotp arser 模块会返 回 结果表明无 法获 取网页, 这 和 示例 网 站 robots. txt 的 定 义一 样。 18 1.4 编写第一个网络爬虫 为了将该功能集成到爬虫中, 我们需要在cra wl 循环中添加该检查。 while crawl queue : url = crawl_queue . p。p () # check url passes robots.txt restrictions if rp . can_fetch (user_agent, url): else: print ’Bl。cked by robots.txt: ’, url 支持代理 有时我们需要使用代理访 问某个网 站 。 比如,Net fli x 屏蔽了美国以外的 大多数国家。使用urllib2支持代理并没有想象中那么容易(可以尝试使用 更友好的 Python HTTP模块re quests来实现该 功能, 其文档地址为 ht tp ://docs.p ython -reque sts.org /)。 下面是使用urllib2支持代 理的代码。 proxy = opener = urllib2 .build_opener () pr。xy params = { urlparse. urlparse (url) .scheme : proxy} 。pener. add handler (urllib2 . ProxyHandler (pr。xy_params)) response = opener.。pen ( request) 下面是集成了该功能的新版本d ow口l oad函数。 def download (url, user_agent= ’ wswp’, proxy=None, num_retries=2) : print ’Downloading:’, url headers = {’User-agent ’: user_agent} request = urllib2 . Request (url, headers=headers) opener = urllib2 . build一。pener () if proxy: try: proxy params = { urlparse.urlparse (url) .scheme : proxy} opener. add handler (urllib2 . Pr。xyHandler (proxy params) ) html = opener. 。pen (request) . read () except urllib2 . URLError as e: print ’Downl。ad error: ’, e.reason 19 第 1 章 网络爬虫简介 html = None if num retries > 0: if hasattr(e , ’code ’) and 500 <= e.code < 600: # retry SXX HTTP errors html = download(url, user agent, proxy, num retries-1) return html 下载F民速 如果我们爬取网站的速度过快,就会面临被封禁或是造成服务器过载的风 险。 为了降低这些风险, 我们可以在两次下载之间添加延时, 从而对爬虫限 速。 下面是实现了该功能的类的代码。 class Throttle: ”””Add a delay between downloads to the same domain def ̿init̿ (self, delay) : # amount of delay between downloads for each domain self.delay = delay # timestamp of when a domain was last accessed self.domains = {} def wait(self, url) : domain = urlparse.urlparse(url) .netl。c last_accessed = self. d。mains . get(domain) if self.delay > 0 and last accessed is n。t None : sleep secs = self.delay - (datetime .datetime . now() - last accessed) .seconds if sleep secs > 0: # domain has been accessed recently # so need to sleep time .sleep(sleep secs) # update the last accessed time self. d。mains [ d。main] = datetime .datetime . now() T hrot tl e 类记录了 每个域 名上 次访 问 的时间 ,如果当前时间距离上 次 访问时间小于指定延 时, 则执行睡眠操作。 我们可以在每 次下载之前调 用 Thr ott le 对 爬虫 进行限 速。 20 1.4 编写第一个 网络爬虫 thr。ttle = Throttle (delay) throttle.wait (url) result = download (url, headers, proxy=proxy, num retries=num retries) 避免爬虫陷阱 目前,我们的爬虫会跟踪所有之前没有访问过的链接 。但是,一些网 站会 动态生成页面内容, 这样就会出现无限多的网页。 比如, 网 站 有一个在线日 历功能, 提供了可以访问下个月和下一年的链接, 那么 下个月的页面中同样 会包含访问 再下个月的链接, 这样页面就会无止境地链接下去。 这种情况被 称为爬虫陷阱。 想要避免陷入爬虫陷阱,一个简单的方法是记录到达当前网 页经过了 多少 个链接, 也就是深度。 当到达最大深度时, 爬虫就不再向队列中添加该网页 中的链接了。 要实现这一功能, 我们需要修改 se en 变量。 该变量原先只记 录访问过的网页链接, 现在 修改为一个字典, 增加了页面深度的记录。 def link crawler ( ... , max depth=2): max depth = 2 seen = {} .. .. 、ns tk pn e- - 1』 d l l一 rxn ua -l rLm 口电k e= n e- -- l sl h =t r p。 he f td p ef di if link not in seen: ) k 14n ·工 +14 ( hd tn pe ep dp a =- e ЍU 『ke nu iq l一 『L 14 nw ea er sc 现在有了这一功能,我们就有信心爬虫最终一定能够完成。如果想要禁用 该 功能, 只需将 ma x_de p th 设为一 个 负 数 即可 , 此时 当前深度永远不会与 之相等。 最终版本 这个 高级链接 爬 虫 的 完 整 源 代 码 可 以在 ht tp s: //bitbucke t. org/ wswp /code/src/t ip/cha pte r01 /link_ cra wler3 .p y 下载得到 。 要测 21 第1 章 网络爬虫简介 试这段代码,我们可以将用户代理设置 为 BadCrawler, 也就是本章前文所 述的被robots.txt 屏蔽了的那个用户代理。 从下面的运行结果中可以看出, 爬虫果然被屏 蔽了, 代码启动后马上就会结束。 》> seed_url = ’http: //ex四iple . webscraping . c。m/index' 》> link_regex = ’/(indexlview) ’ 》> link_crawler ( seed_url , link_regex , user_agent= ’ BadCrawler ’} Blocked by robots .txt : http ://example . webscraping .com/ 现在,让我们使用默认的用户代理, 并将最大深度设置为1 , 这样只有主 页上的链接才会被下载。 》> link」crawler(seed_url , link_regex , max_depth=l) D。wnl。ading : http ://example . webscraping .com//index Downl。ading : http://example . webscraping .c。m/index/l Downl。ading : http://example . webscraping . com/view/Antigua-and-Barbuda-10 D。wnl。ading : http : //exa皿1ple . webs craping . c。m/view/Antarctica-9 D。wnloading : http://example . webscraping .c。皿/view/Anguilla-8 Downloading : http://example . webscraping . com/view/Ang。la-7 Downloading : http ://example . webscraping . com/view/Andorra-6 D。wnloading : http : //ex皿1ple . webscraping . c。m/view/阳.erican-S础。a-5 D。wnloading : http ://ex四ple . w,由scraping . com/view/Algeria-4 Downloading: http://example . webscraping . com/view/Albania-3 D。wnl。ading: http ://examp le . webscraping . com/view/Aland-工slands -2 D。wnl。ading: http://example . webscraping . c。m/view/Afghanistan-1 和预期一样, 爬虫在下载完国家列表的第一页之后就停止了。 1.5 本章小结 本章介绍了网络爬虫,然后开发了一个能够在后续章节中复用的成熟爬 虫。 此外,我们还介绍了一些外部工具和模 块的使用方法, 用于了解网 站 、 用户代理、 网站地图、 爬取延时以及各种爬取策略。 下一章 中,我们将讨论如何从己爬取到的网页中获取数据。 22 第2章 数据抓取 在上一章中 ,我们构建了一个爬虫, 可以通过跟踪链接的方式下载我们所 需的网页。 虽然这个例子很有意思 , 却不够实用, 因为爬虫在下载网页之后 又将 结果丢弃掉了。 现在, 我们需要让这个爬虫从每个网页中抽取一些数据, 然后实现某些事情, 这种做法也被称为抓取(scraping) 。 首先, 我们会介绍一个叫做Firebug Lite 的浏 览器扩展, 用于检查网页内 容, 如果你有一些网络开发背景的话, 可能己经对该扩展十分熟悉了。 然后, 我们会介绍三种抽取网 页数据 的方法, 分别是正则表达式 、 Beauti削 Soup和 lxml 。最后, 我们将对比这三种数据抓取 方法 。 2.1 分析网 页 想要了解一个网页的结构如何, 可以使用查看源代码的方法。 在大多数浏 览器中, 都可以在页面上右键单击选择 View page source 选工页, 获取网页的 源代码, 如图 2.1 所示。 我们可以在 H⬍的下述代码中找到我们感兴趣 的数据 。
Hon'.e 3号J「ci飞 l Lc;i.:ir1 • National Flag: ꇍ Area: 244,820 square kilorne甘es Population: 62,348.447 lso GB Country: Unit叫 Back Capital: London ForNa•d Continent: EU Reload Tld .uk Currency C创e GBP Save as .. Currency Name Pound Print Phone: 44 Translate to Englisl1 Postal Code Fo『mat: @##@@t@ #@@!@#@! View pag Postal Code Reg鰂'(([.舟苟明勾i {2}\d{2}[A Z]! .�, 'I '¥- ns因ct w1ll1 Firebug Lile Z)\d[A'Z阴阳 ’ (GIROAA)四 ”JSONView • Languages: en-GB,cy-GI � user-Agent Switcher Neigh也ours: IE Edit 图2.1 Inspect element 、 对于浏览器解析而言 ,缺失空白符和格式并无大碍, 但在我们 阅读时则会造 成一 定困难 。 要想更好地理解该表格, 我们将使用 Firebug Lite 扩展。 该扩展适 用于所有浏览器,我们可以通过 https ://getfirebug.com/firebuglite 页面获取到该扩展。 如果愿意的话, Firefox 用户可以安装完整版的 Firebug 24 2.1 分析网页 扩展, 不过Lite 版本己经包含了 我们在本章和第 6 章中所用 到的功能。 Firebug Lite 安装完成后, 可以右键单击我们在抓取中感兴趣的网页部分, 然后在菜单 中选择Ins p ect with Firebug Lite ,如图2.2 所示 。 National Flag: Area: Population. lso: Country: Capital: Continent: Tld: 留罢 、、J HU MM fJ AU MX w m Ha E e H B F R O L民e 盯 !cil-- U叶 阳 nu17k u sa斗 -WH 34 6 K 问Mm d m山 I MF Bd HHU B 川m u nd 缸GU U E .llk Save as. Cu厅ency Code: GBP Currency Name: Pound Phone: Languages: Neighbou「B Edit Prmt... Translate to English View page soume Vi四'/page info en-GB,cy-G Bj 、I User-Agent Switche「 IE Inspect element 图2. 2 此时, 浏览器就会打开如图2.3 所示的 Firebug 面板, 并显示选中元素周 围的 HTML层次结构。 如图2.3 所示, 当选择国 家面积这一属性时, 我们可以从 Firebug 面板中 清晰地看到, 该值包含在 class 为 w2p fw 的<td>元素中, 而<td>元素又是 ID 为 pl aces area ro w 的<tr>元素的子元素。 现在 , 我们就获取 到需要 抓取的面积数据 的所有信息了。 25 第2章数据抓取 拼 Inspect cons脚1可m Script DOM 百飞际aa> 臼 <body data -f<.>edly- mini;" yes"> 臼<div class="navbar navb盯- inve rse”〉 曰<div class="container、 日<section id=可nain" class=阴阳in row与 日<div class=· span旦、 国<form action=”#” enctype=飞ultipart/ fonn data” mεthod=’'post”〉 曰<table> 曰<tbody> 图2.3 2.2 三种网 页抓取方法 现在我们已经了解了该网页的结构 , 下面将要介绍三种抓取其中数据的方 法。 首先是正则表达式, 然后是流行的 BeautifulSoup模块, 最后是强大 的lxml 模块。 2.2.1 正则表达式 如果你对正则表达式还不熟 悉, 或是需要一些提示时禽, 可以查阅 https ://docs .pytho日 .org/2/howto/regex . html 获得完整 介绍。 当我们使用 正则表达式抓取面积数据时, 首先需要尝试匹配<td>元素中 的内容, 如下所示。 》〉 工mport re 》> url = ’http://example.webscrapi口g . com/view/Un工ted -Kingdom 239’ 》> html = download (url) >>> re . findall (’<td class=”w2p f w”> ( . *?)
标签。 要想分离出面积属性, 我们可以只选择其中的第二个元素, 如下所示。 》> re . findall (’<td class=”w2p f w”> (.•?) </td> ’, html) [l] ’244,820 square kil。metres ’ 虽然现在可以使用这个方案, 但是如果网页发生变化, 该方案很可能就 会失效。 比如表格发生了变化, 去除了第二行中的 国土面积数据。 如果我 们只在现在抓取数据, 就可以忽略这种未来可能发生的变化。 但是, 如果 我们希望未来还能再次抓取该数据, 就需要给出 更加健壮的解决方案, 从 而尽可能避免这种布局变化所带来的影响。 想要该正则表达式更加健壮 , 我们可以将其父元素< tr>也加入进来。 由 于该元素具有 ID 属性, 所以应 该是唯一的。 》> re . findall (’<tr id=”places area row”><td class=”w2p fl” ><label for=”places area” id=”places area label”>Area :