爬虫项目经验总结

前言

  从新数据库 mongodb 到基于内存的 key-value 数据库 Redis,从 scrapy 爬虫框架到 re 正则表达式模块,尤其正则,以前不会写的时候总是依赖 string 的各种方法,部分时候显得有些繁琐,会正则了之后在字符串的匹配、查找、替换、分隔方面打开了另一扇便捷之窗。另外,将 Redis 引入爬虫架构来实现分布式,也算是一个技术理念的突破吧,也为后面研究高并发站点打下了基础。
 
  其实前面说了那么多唬人的东西,用的时候倒是没有说有实在适应不来的,作为一个敲了快三年代码的砖工来说算不上什么技术攻坚,可能在 Nosql 和正则模块(包括正则语法和 re 模块)投入的精力相对多一点。业务上,对于网页结构的分析和一个高覆盖率 url 爬取策略的制定还是得跟有经验的开发人员多讨论,就爬虫来说,技术不是重点,更多的是对站点页面跳转套路的分析。
  
  下面就结合实际开发中常遇到的一些问题分节对爬虫这块的注意事项做一个简要讲解。

取消合法性检验以改进爬取方案

  我这里要讲的是在确认程序语法无误后,以故意制造 AtttributeError 来完善爬取策略,算不上什么方案,一点写法上的小技巧吧。

  这个报错具体解释起来就是 'NoneType' object has no attribute ,类似于 java 里面的空指针异常。首先,解决这个异常的方式很简单,比如让 soup 在查找上一级标签的时候做一次是否为空的判断,如果不为空则继续查找到下一级目标:(if parentNode:   ...)。但实际上最开始写这块内容的时候要避免这种条件判断,因为我们并不知道我们要找的目标在该类网页当中是否只存在一种结构,有可能有的页面压根儿没那个标签,也有可能标签名不一样或者类名变了等等情况,这需要我们在测试过程中去记录下所有出错的页面,逐一排查这些页面的特殊结构,这样才能有效地保证我们所爬取的页面覆盖面更广,数据更全。下面拿实际代码举个例子:

soup = BeautifulSoup(response.body_as_unicode(),'lxml')
citys = soup.find('div',class_="piList").find_all('span') for city in citys:   href = city.find('a').get('href')
  yield Request(href, callback=self.get_url)

  这里在获取网页中城市的链接的时候,并没有首先去对 span 的祖先(前端术语) class 为 piList 是否存在做判断,因为在爬这类的网页的时候当然会有可能说是部分页面的城市信息压根不放在这里面,类名为 piList 的标签压根不存在,这时候程序会在这里报 AtttributeError 但不会影响爬虫的继续运行,等整个程序执行完毕,scrapy 会有一个总的出错统计在日志的末尾,我们就顺着总数一一找出出错位置对应的页面 url 再去浏览器找到该页面,重新分析其文档结构,或改类名,或改标签名,最后完善成类似下面的代码:

soup = BeautifulSoup(response.body_as_unicode(),'lxml')
city_list = soup.find('div',class_="piList")
if not city_list:
  city_list = soup.find('div',id="citys")
citys = city_list.find_all('span') for city in citys:   href = city.find('a').get('href')   yield Request(href, callback=self.get_url)

关于使用浏览器开发者工具查看网页

  我们在用浏览器的开发者工具分析网页的时候,最好是在 Sources 下面查看网站源码,因为我们爬虫爬下来的内容是未经浏览器渲染的(尽管有这样的工具,比如 PyQt 中的 webkit)。但是大多数情况下我们在 Elements 审查/检查 窗口看到的内容与 Sources 下面是一致的,而且在这下面进行标签的查看和文档结构的分析也更方便,所以可在其下面定位目标后到 Sources 中确认是否能找到,再确定 beautifulsoup 或 xpath 的写法,尤其对于翻页和通过事件响应跳出的弹窗。
 
 
 
posted @ 2017-04-01 14:20  笑叹词穷  阅读(5283)  评论(0编辑  收藏  举报