- 小莫想要某站上全部的电影,写了标准的爬虫(基于HttpClient库),不断地遍历某站的电影列表页面,根据 Html 分析电影名字存进本身的数据库。
- 这个站点的运维小黎发现某个时间段请求量陡增,分析日志发现都是 IP(xxx.xxx.xxx.xxx)这个用户,而且 user-agent 仍是 Python-urllib/2.7 ,基于这两点判断非人类后直接在服务器上封杀。
- 小莫电影只爬了一半,因而也针对性的变换了下策略:1. user-agent 模仿百度("Baiduspider..."),2. IP每爬半个小时就换一个IP代理。
- 小黎也发现了对应的变化,因而在服务器上设置了一个频率限制,每分钟超过120次请求的再屏蔽IP。 同时考虑到百度家的爬虫有可能会被误伤,想一想市场部门每个月几十万的投放,因而写了个脚本,经过 hostname 检查下这个 ip 是否是真的百度家的,对这些 ip 设置一个白名单。
- 小莫发现了新的限制后,想着我也不急着要这些数据,留给服务器慢慢爬吧,因而修改了代码,随机1-3秒爬一次,爬10次休息10秒,天天只在8-12,18-20点爬,隔几天还休息一下。
- 小黎看着新的日志头都大了,再设定规则不当心会误伤真实用户,因而准备换了一个思路,当3个小时的总请求超过50次的时候弹出一个验证码弹框,没有准确正确输入的话就把 IP 记录进黑名单。
- 小莫看到验证码有些傻脸了,不过也不是没有办法,先去学习了图像识别(关键词 PIL,tesseract),再对验证码进行了二值化,分词,模式训练以后,总之最后识别了小黎的验证码(关于验证码,验证码的识别,验证码的反识别也是一个恢弘壮丽的斗争史...),以后爬虫又跑了起来。
- 小黎是个不折不挠的好同窗,看到验证码被攻破后,和开发同窗商量了变化下开发模式,数据并再也不直接渲染,而是由前端同窗异步获取,而且经过 JavaScript 的加密库生成动态的 token,同时加密库再进行混淆(比较重要的步骤的确有网站这样作,参见淘宝和微博的登录流程)。
- 混淆过的加密库就没有办法了么?固然不是,能够慢慢调试,找到加密原理,不太小莫不许备用这么耗时耗力的方法,他放弃了基于 HttpClient的爬虫,选择了内置浏览器引擎的爬虫(关键词:PhantomJS,Selenium),在浏览器引擎运行页面,直接获取了正确的结果,又一次拿到了对方的数据。
- 小黎:.....
一般状况下,在爬虫与反爬虫的对弈中,爬虫必定会胜利。javascript
换言之,只要人类可以正常访问的网页,爬虫在具有同等资源
的状况下就必定能够抓取到。css
这篇文章就够了:携程技术中心 - 携程酒店研发部研发经理崔广宇 <爬虫与反爬虫> 技术分享html
JavaScript 是网络上最经常使用也是支持者最多的客户端脚本语言。它能够收集 用户的跟踪数据,不须要重载页面直接提交表单,在页面嵌入多媒体文件,甚至运行网页游戏。前端
咱们能够在网页源代码的<scripy>
标签里看到,好比:java
jQuery 是一个十分常见的库,70% 最流行的网站(约 200 万)和约 30% 的其余网站(约 2 亿)都在使用。一个网站使用 jQuery 的特征,就是源代码里包含了 jQuery 入口,好比:python
若是你在一个网站上看到了 jQuery,那么采集这个网站数据的时候要格外当心。jQuery 可 以动态地建立 HTML 内容,只有在 JavaScript 代码执行以后才会显示。若是你用传统的方 法采集页面内容,就只能得到 JavaScript 代码执行以前页面上的内容。jquery
咱们与网站服务器通讯的惟一方式,就是发出 HTTP 请求获取新页面。若是提交表单以后,或从服务器获取信息以后,网站的页面不须要从新刷新,那么你访问的网站就在用Ajax 技术。程序员
Ajax 其实并非一门语言,而是用来完成网络任务(能够认为 它与网络数据采集差很少)的一系列技术。Ajax 全称是 Asynchronous JavaScript and XML(异步 JavaScript 和 XML),网站不须要使用单独的页面请求就能够和网络服务器进行交互 (收发信息)。web
Ajax 同样,动态 HTML(Dynamic HTML, DHTML)也是一系列用于解决网络问题的 技术集合。DHTML 是用客户端语言改变页面的 HTML 元素(HTML、CSS,或者两者皆 被改变)。好比页面上的按钮只有当用户移动鼠标以后才出现,背景色可能每次点击都会改变,或者用一个 Ajax 请求触发页面加载一段新内容,网页是否属于DHTML,关键要看有没有用 JavaScript 控制 HTML 和 CSS 元素。数据库
那些使用了 Ajax 或 DHTML 技术改变 / 加载内容的页面,可能有一些采集手段。可是用 Python 解决这个问题只有两种途径:
Selenium是一个Web的自动化测试工具,最初是为网站自动化测试而开发的,类型像咱们玩游戏用的按键精灵,能够按指定的命令自动操做,不一样是Selenium 能够直接运行在浏览器上,它支持全部主流的浏览器(包括PhantomJS这些无界面的浏览器)。
Selenium 能够根据咱们的指令,让浏览器自动加载页面,获取须要的数据,甚至页面截屏,或者判断网站上某些动做是否发生。
Selenium 本身不带浏览器,不支持浏览器的功能,它须要与第三方浏览器结合在一块儿才能使用。可是咱们有时候须要让它内嵌在代码中运行,因此咱们能够用一个叫 PhantomJS 的工具代替真实的浏览器。
能够从 PyPI 网站下载 Selenium库https://pypi.python.org/simple/selenium ,也能够用 第三方管理器 pip用命令安装:
pip install selenium
Selenium 官方参考文档:http://selenium-python.readthedocs.io/index.html
PhantomJS 是一个基于Webkit的“无界面”(headless)浏览器,它会把网站加载到内存并执行页面上的 JavaScript,由于不会展现图形界面,因此运行起来比完整的浏览器要高效。
若是咱们把 Selenium 和 PhantomJS 结合在一块儿,就能够运行一个很是强大的网络爬虫了,这个爬虫能够处理 JavaScrip、Cookie、headers,以及任何咱们真实用户须要作的事情。
注意:PhantomJS 只能从它的官方网站http://phantomjs.org/download.html) 下载。 由于 PhantomJS 是一个功能完善(虽然无界面)的浏览器而非一个 Python 库,因此它不须要像 Python 的其余库同样安装,但咱们能够经过Selenium调用PhantomJS来直接使用。
PhantomJS 官方参考文档:http://phantomjs.org/documentation
Selenium 库里有个叫 WebDriver 的 API。WebDriver 有点儿像能够加载网站的浏览器,可是它也能够像 BeautifulSoup 或者其余 Selector 对象同样用来查找页面元素,与页面上的元素进行交互 (发送文本、点击等),以及执行其余动做来运行网络爬虫。
Selenium 的 WebDriver提供了各类方法来寻找元素,假设下面有一个表单输入框:
那么:
By ID
实现
By Class Name
实现
By Tag Name
实现
By Name
实现
By Link Text
实现
By Partial Link Text
实现
By CSS
实现
By XPath
实现
有些时候,咱们须要再页面上模拟一些鼠标操做,好比双击、右击、拖拽甚至按住不动等,咱们能够经过导入 ActionChains 类来作到:
示例:
咱们已经知道了怎样向文本框中输入文字,可是有时候咱们会碰到<select> </select>
标签的下拉框。直接点击下拉框中的选项不必定可行。
Selenium专门提供了Select类来处理下拉框。 其实 WebDriver 中提供了一个叫 Select 的方法,能够帮助咱们完成这些事情:
以上是三种选择下拉框的方式,它能够根据索引来选择,能够根据值来选择,能够根据文字来选择。注意:
- index 索引从 0 开始
- value是option标签的一个属性值,并非显示在下拉框中的值
- visible_text是在option标签文本的值,是显示在下拉框的值
所有取消选择怎么办呢?很简单:
当你触发了某个事件以后,页面出现了弹窗提示,处理这个提示或者获取提示信息方法以下:
一个浏览器确定会有不少窗口,因此咱们确定要有方法来实现窗口的切换。切换窗口的方法以下:
也可使用 window_handles 方法来获取每一个窗口的操做对象。例如:
操做页面的前进和后退功能:
获取页面每一个Cookies值,用法以下
删除Cookies,用法以下
注意:这是很是重要的一部分!!
如今的网页愈来愈多采用了 Ajax 技术,这样程序便不能肯定什么时候某个元素彻底加载出来了。若是实际页面等待时间过长致使某个dom元素还没出来,可是你的代码直接使用了这个WebElement,那么就会抛出NullPointer的异常。
为了不这种元素定位困难并且会提升产生 ElementNotVisibleException 的几率。因此 Selenium 提供了两种等待方式,一种是隐式等待,一种是显式等待。
隐式等待是等待特定的时间,显式等待是指定某一条件直到这个条件成立时继续执行。
显式等待指定某个条件,而后设置最长等待时间。若是在这个时间尚未找到元素,那么便会抛出异常了。
若是不写参数,程序默认会 0.5s 调用一次来查看元素是否已经生成,若是原本元素就是存在的,那么会当即返回。
下面是一些内置的等待条件,你能够直接调用这些条件,而不用本身写某些等待条件了。
隐式等待比较简单,就是简单地设置一个等待时间,单位为秒。
固然若是不设置,默认等待时间为0。