内容已更新2019.2.16


​ 一个跟导师做的项目需要拼多多的数据,就当练练手。爬完最后拿到了100万+的商品数据。(大概需要跑一天吧)github地址

​ 没有使用scrapy或者pyspider一类的框架,因为我这个拼多多爬虫实现的方法其他都不大同。拼多多虽然没有网页版,但是有一个这个www.yankeduo.com,和app端是一模一样的,简化了抓app包分析的过程,直接分析这个网站就可以了。这个项目主要是使用selenium + 代理实现的,用selenium模拟点击,从代理中截取流通的数据包。

页面分析

​ 爬虫主要是爬取搜索界面的所有的分类,再从分类进去爬去所有商品信息。浏览器打开开发者工具,随便点击一个小分类进去

​ 就可以看到RESTful API返回的商品信息,URL类似于http://apiv3.yangkeduo.com/v4/operation/3376/groups?offset=0&size=100&opt_type=2&sort_type=DEFAULT&list_id=XXX&anti_content=XXX&pdduid=XXX而如果我们直接修改URL参数是无法正常获取数据的(就算修改size都不行)。

​ 整个URL唯一无法破解的就是那个anti_content字段,强行破解了一下午无果后决定用selenium来模拟点击。但它并不是所有信息都会放在最终页面上的,所以如果仅仅使用selenium是无法得到所有数据的。然而这么好的RESTful API也不能浪费了,于是决定采用selenium连接代理,模拟点击的过程中从代理中获取到API返回的商品数据。

选择代理

​ 一开始想自己搭http代理,但是后来发现还得加上https。太麻烦了,学习成本有点高,决定去github上找轮子。不久就找到了一个BaseProxy,使用简单,也有开发的接口,集成到项目中很方便。

开始爬取

pddSpider

├── core │ ├── captcha.py │ ├── dboperate.py │ ├── proxy.py │ ├── seleniumoperate.py │ ├── state │ └── threadpool.py ├── README.md └── Spider.py

​ 主要文件是Spider.py和seleniumoperate.py。

​ Spider.py继承proxy.py(也就是BaseProxy代理项目),运行代理,指定拦截函数拦截特定response,将返回的商品信息解析存入数据库。

​ seleniumoperate.py 就是模拟点击操作,首先先连接代理,并实现了对于搜索分类页面所有的小分类的调度爬取,_loadState()和_saveState()函数为了保存状态断开不用重新爬取了。

​ 其他文件就如文件名一样的直接,capcha.py功能是与打码平台交互,dboperate.py是数据库操作类,threadpool.py是线程池类。state是保存的状态文件。

​ 代码都在github上,可以去看看。

验证码问题

​ 验证码确实是一个烦人的问题,自己写的很简单的普通orc识别几乎识别不了。在github上找到的一些机器学习识别验证码的项目大都需要几万张图片来训练,才能达到比较好的效果,但是让我一个人手动打码打几万张是不大可能的。所以最后还是选择了网上的打码平台。经过优化访问后可以实现五到八分钟才出现验证码。这样我花了五六块钱左右的网络打码费就把商品全部爬下来了。

​ 在seleniumoperate.py中实现了验证码的CaptchaMonitor类。当一个号多次访问是,它会出现提交了验证码后不断的重复出现验证码,无休止的提交验证码。所以注册了7个号来随机使用,在CaptchaMonitor类中也实现了抑制短时间重复提交验证码的措施,防止白白使用了打码次数。

selenium 爬取被识别问题

​ 在我发完这篇博客后,有很多朋友也尝试了我github上的代码。后来我发现,拼多多增加了一些反爬策略,我的代码已经被拼多多的反爬策略过滤了。作为一个好学的同学,我当然要深入研究一下啦。

​ 首先,selenium+geckodriver 是通过模拟火狐浏览器访问的,以此欺骗目标网站就好像是人为点击的一样。可是当我再跑我的代码时,发现人工点击和selenium效果是不一样的,当使用selenium模拟时,不断会出现错误界面。经过查询,selenium在运行的时候会暴露出一些预定义的Javascript变量(特征字符串),例如"window.navigator.webdriver",在非selenium环境下其值为undefined,而在selenium环境下,其值为true(如下图所示为selenium驱动下Chrome控制台打印出的值)。当然,还有其他很多变量,大家可以看看这篇文章

​ 那么我们重新理清思路,我们通过selenium模拟点击并连接代理,从代理中截取商品数据。而拼多多通过js文件判断我们是否使用selenium,并且将判断结果发送给服务器,控制返回内容。我们很难找到判断结果是以何种方式发送给服务器的。但我们可以从代理中截取该js文件,改变其内容,将判断selenium在js中预设的变量的部分删除掉就行了。

所以我在新代码中添加了一些代码:

if 'react_psnl_verification_' in response.request.path:
    js_body = str(response.get_body_data(), 'utf-8')
    js_body =  js_body.replace("navigator.webdriver", "navigator.qwerasdfzxcv")
    response.set_body_data(bytes(js_body, 'utf-8'))