接着上一篇继续聊scrapy。
断断续续的使用scrapy已经很长时间,在各种问题也算是有所领悟(所需工具firefox,firebug,firefox的xpath插件)。
1. 非标准的html解析问题(上一篇提到过),这个问题蛋疼不是问题有多难解决,而是很难定位到错误的原因。在firefox中确保xpath没有写错的情况下,找不到相应的数据,这个时候就要考虑是否是这个原因,直接查看页面源码,如果有不标准的html,在firefox中会有红色标识。
selector = XPathSelector(text=response.body.replace('<div class="left-clear"/>','<div class="left-clear">'))
2. 通常的情况下,一些流量比较大的网站都有反爬虫的机制,避免恶意的访问,减轻服务器的压力。一般的情况的下调整抓取的间隔,更换代理。2种方式都有缺点。
抓取间隔设置download_delay=1,通常设置这个属性,整个spider的性能变得非常低。
更换代理,可以到http://pachong.org/去找免费的代理,一般速度也不快,而且不稳定。
3. http code欺骗(我自己取得名字),一般情况数据的正常返回都是20x,scrapy会自动忽略掉50x的http code。这个(http://www.travelplus.cn/plus/list.php?tid=8
)用浏览器打开的时候,你看不出任何问题。用firebug就能清晰的看到,其实它是将500页面,当做正常页面显示。
在spider里添加handle_httpsatstus_code = [500],一切照旧即可。
4. 模拟浏览器请模拟彻底。如果你有服务器点开发的经验,就不难理解,服务器可以通过任何一个headers或者参数来屏蔽你的request。参数自然不用说,能带上都带上,至于一些参数加密,以后再提。主要是针对headers中的几个常用的。
Cookie,这个最常见用的,平心而论scrapy对cookie的支持只是基础的支持,用起来不太好用。
User-Agent, 这个主要还是服务器为了区分request是来自pc端还是手机端,会导致response不一样。
Referer, 防止伪造的跨网站请求。
X-Requested-With=XMLHttpRequest, 这个很好理解,对于同一个url,ajax request和普通request的response结果不一样很正常
Content-Length, 一些网站没有这个header会返回411
以上5个header基本上能解决问题,万一遇到顽固份子,那只好彻底的模仿吧。
5. 不要忽略firebug里监控到任何http request,部分网站在提交数据的时候,会先跳转到一个页面,这个页面是空白的页,只是包含一些隐藏的表单,最后用js带上表单跳转到其他的页面。因此在firebug的监控的request中,会莫名其妙的多出来一些参数。
6. xpath表达式,能简单尽量简单,尽量class和id来表达。千万别将xpath的表达式依赖很多属性,这样难以维护不说,而且极不稳定。
7. xpath表达式能在浏览器中找到元素,在spider里确不能,有可能性js动态加载,还有table元素,有的会自动添加th等。这个时候请直接对着html源码写你的表达式。
8. ajax request和json,这应该是现在网站的主流。要关注的真正得到数据的request,通常我们要抓取的数据也都在ajax request中,拿到数据后用json.loads(response.body)
9. 要抓取的内容分布在不同的request中,这个时候request meta就能很好的胜任。
def parse_city_item(self, response): x = HtmlXPathSelector(response) item = Item() //给item赋值 item['title'] = ''.join(x.select('//div[@class="title"]/text()').extract()) data = {} req = FormRequest(url=url,formdata=data,callback=self.parse_comment) req.meta['item'] = item //带上返回req return req def parse_comment(self, response): item = response.request.meta['item'] x = HtmlXPathSelector(response) item['content'] = ''.join(x.select('//div[@class="content"]/text()').extract()) return item
10. 增量抓取,很多情况下需要抓取网站更新的内容。我们知道在一次抓取的过程是能避免重复抓取,scrapy默认提供文件存储的方式,只需要再settings里设置JOBDIR="path"。我在使用scrapy还是0.9,没有这个特性,使用redis作为url存储。个人感觉对于大规模抓取用redis还是比文件的方式要好很多。redis里可以设置key的过期时间,肯定会有人说,这样能保证不重复的抓取吗,当然不能绝对,但是可以调整抓取深度,对于抓取较为频繁网站,抓取到相同的概率就很低。比如说抓取sina的体育新闻,将url做md5加密存储到redis里,过期时间设置为1天,抓取体育新闻滚动页面前3页(http://roll.sports.sina.com.cn/s/channel.php?ch=02#col=64&spec=&type=&ch=02&k=&offset_page=0&offset_num=0&num=60&asc=&page=1),15分钟抓取一次。而单纯用文件方式存储的话,文件只大不小,多了自然影响性能。
11. 编码,上一篇也提到过。scrapy为我们做了编码,但是如果这样好错了,就需要特殊处理了。