一个爬虫项目记录

一个爬虫项目记录

原文:http://ichuan.net/post/59/一个爬虫项目记录/

上周自己做了个小项目,爬某个网站上的数据,存入 mysql。

最开始是这么计划的:从一个入口出发(比如分类页面),多线程抓取网页,然后用 lxml 定位 dom,获取想要的部分,入库。

组件

想法是最好利用成熟的组件,这样自己写的代码少,出的问题也少。

线程池:本来公司有成熟的线程池 lib,因为是个人项目,所以没使用。pypi 上找了几个都觉得对这个项目来说有些复杂。后来自己简单实现了一个(simple_threadpool),经过实践检验,够用。

dom 解析:最开始打算用 xpath 来从 html 中取数据,但是觉得语法很晦涩。后来想起以前看到过类似用 css 选择器来查询 html 的,找了下,是 pyquery。经过实践检验,很强大,绝大部分 css 都支持。

orm:决定了数据存入 mysql,就需要个 orm 来操作数据。希望有像 django 中类似的。我比较排斥sqlalchemy,太复杂,每次查文档都得查半天。后来找到了一个轻量级的:peewee。好用。

http 请求:直接用的 urllib2,可以很简单地设置 ua 和 超时,而且调试方便。

编码,执行

程序和数据库设计好后,就开始编码了。完毕后执行,看日志,停掉,改 bug,清数据,执行,看日志。。。

中间部分网页用了 bigpipe,数据都在放在了 js 中。我只好字串定位到 js 函数入口,把 json 参数抠出来然后反序列化,之后再做 dom 解析,用 pyquery 取数据。

几次迭代下来,代码比较稳定了,数据也在线性增长。很满意。可好景不长,几小时后日志里大量报错,排查后发现对方网站把我 ip 封了。现在连浏览器也打不开那个网站了。

第二版

被封是个原因,速度慢是另一个原因,有的地方几乎抓好几个网页才能拼凑成一份数据入库。考虑到这些,我开始重新编码。

为求效率,不能再老老实实抓网页了。我首先想到了该网站的手机版。手机版一般来说 dom 相对干净简单,好分析。倒是有手机版网页,但只是一些简单数据,不全。然后想到了 app 版本。看看 app 中用的是哪里的数据,方便的话就用它了。

我下来了它 android 版的 apk,unzip 后打开 classes.dex,用 http、api 等关键字搜果然发现了一些 url。看起来像是 api 服务器地址。用 Genymotion 建了个 android 虚拟机,安装 app,并在系统设置里把网络代理设置成本机用 tinyproxy 搭建的一个小 socks5 代理。然后在虚拟机里打开 app,随机浏览,看到 tinyproxy 日志里出现了一些请求。

用浏览器试了试,这些 api 请求居然都没做客户端验证,太省心了。立马把代码中解析网页部分换成了直接抓 api 的。这样带来了几个好处:

  1. 用 api 可以获取大量数据,比以前的方式少了很多请求,入库速度加快
  2. api 返回 json 数据,不需要 pyquery 和 lxml 解析,代码量骤减
  3. api 返回的数据字段非常全,我估计就是他们数据库里的字段了
  4. 好像为了做负载均衡,app 内置好几个 api 节点。其中某个没有做请求数限制,导致我抓了整整一天都没被封

最终按这个方式,用家里小网络一天时间把全站数据都抓下来了。

经验教训

每次编码都会有收获:

  1. urllib2 默认没有超时时间。需要在 urlopen 时手动指定。没注意到这个导致程序后期卡死,查了半天还查不出来什么原因。后来加了超时,做了重试机制,就再没遇到卡死的问题了。
  2. http 响应可能不全:遇到过几次 IncompleteRead 报错,是由于网络不稳定导致的。之前用浏览器下载文件也遇到过。明明几十M的压缩包下下来看到只有1M,还可以正常打开。把这个报错当作异常,用重试机制可以解决。
  3. 要做静态代码检查:以前只道 js 要用 jshint 检查,并没用 pylint 检查 py 文件的习惯。这次真是血淋淋的教训。跑了好几小时的程序后期忽然报个 “local variable referenced before assignment” 或 “unexpected indent” 低级错误,前功尽弃。
  4. 个人私有代码可以放 bitbucket 上:这样即使是小项目、一次性项目,也可以用 git 托管,当作个备份也划算。屌丝就用免费的 bitbucket,有钱推荐用 github pro。
  5. 能快速搭建环境:用 requirements.txt 和 virtualenv,有个简单的 README,再有个 install.sh 就更好了。方便自己和别人在不同机器上快速搭建运行环境。
  6. 日志:很有必要,最好 stdout 和 stderr 写的是不同类型日志,直接输出,执行时分别重定向到日志文件。
Comments are closed.