使用python比较多的一个应用场景就是数据采集,采集一些比较蛋疼的页面时,会检测用户是否是通过浏览器打开的页面,还有一些会通过js加载后才会回显内容。这个时候可能用的比较多的方案就是python + Headless + 浏览器,其会自动打开浏览器,并输入相应的页面地址后,并可以抓取返回的结果。

不过在linux上经常是没有GUI图形化下跑的,之前有一个开源项目叫PhantomJS ,不过目前该项目已停止了。原因很简单,chrome59版本后加入了 Headless Chrome。Headless Chrome 是 Chrome 浏览器的无界面形态,可以在不打开浏览器的前提下,使用所有Chrome支持的特性,在命令行中运行你的脚本。相对于传统的chrome浏览器,这是一个可以在后台用命令行操作浏览器的工具,对于爬虫编写以及web自动化测试都有很大的作用。相比较同类工具Phantomjs,其更加强大(主要因为其依赖的webkit更新),这也是Phantomjs暂停开发的原因。

一、chrome的安装

这里以centos7为例,直接使用官方源

1# vim /etc/yum.repos.d/chrome.repo
2写入以下内容:
3[google-chrome]
4name=google-chrome
5baseurl=http://dl.google.com/linux/chrome/rpm/stable/$basearch
6enabled=1
7gpgcheck=0
8gpgkey=https://dl-ssl.google.com/linux/linux_signing_key.pub

配置完成后,使用如下命令进行安装:

1yum -y install google-chrome-stable --nogpgcheck

二、Headless Chrome的简单使用

先说下不和selenium或其他python模块配合使用的情况下,该模式的一些简单使用。

1# 输出html:
2google-chrome --headless --dump-dom https://blog.361way.com
3# 将目标页面截图:
4google-chrome --headless --disable-gpu --screenshot https://blog.361way.com
5# 规定大小
6google-chrome --headless --disable-gpu --screenshot --window-size=640,960 https://blog.361way.com
7# 保存为pdf:
8google-chrome --headless --disable-gpu --print-to-pdf https://blog.361way.com

这里使用https地址也是一样的。上面的命令在有些版本下会要求加入–no-sandbox参数,这个可以根据报错信息进行调整。

除了上面的提到的,还有远程调用模式–remote-debugging-port,个人理解这个有点类型于代理映射模式。通过把远程的一个请求映射到本地的一个端口上,方便进行调试管理。使用如下:

1google-chrome --headless --disable-gpu --no-sandbox --remote-debugging-port=9222 --user-data-dir='/d/site' https://blog.361way.com

--user-data-dir参数可以设定保存目录,–user-agent参数可以设定请求agent。上述的命令打开了一个websocket调试接口对当前Tab内页面的DOM、网络、性能、存储等等进行调试。打开 http://127.0.0.1:9222/ 链接可以看到可检查的网页,可以点击它们并看到使用了哪种Headless渲染。其还有一系列的操作接口,如下:

http://127.0.0.1:9222/json 查看已经打开的Tab列表
http://127.0.0.1:9222/json/version : 查看浏览器版本信息
http://127.0.0.1:9222/json/new?http://www.baidu.com : 新开Tab打开指定地址
http://127.0.0.1:9222/json/close/8795FFF09B01BD41B1F2931110475A67 :关闭指定Tab,close后为tab页面的id
http://127.0.0.1:9222/json/activate/5C7774203404DC082182AF4563CC7256 : 切换到目标Tab

上面后两项使用的ID是从第一项 http://127.0.0.1:9222/json 查看已经打开的Tab列表中获取的。tab页面信息中有一个devtoolsFrontendUrl,是开发者工具的前端地址,可以打开:http://127.0.0.1:9222/devtools/inspector.html?ws=127.0.0.1:9222/devtools/page/CE2E627C634EAAE3CE9193DC374C7B4A ,在开发者工具里切换到Performance,勾选Screenshots,点刷新图标,重新加载完成就可以看到逐帧加载的截图。

三、selenium与chrome的整合

Selenium 是用于测试 Web 应用程序用户界面的常用框架,可以使用pip install selenium直接安装。安装完成后,还需要下载对应的浏览器驱动插件,这里以chromedriver为例。下载地址:chromedriver国内镜像。这里下载的时候注意下载的驱动插件要和浏览器相应的版本相对应。

来一个打开本站点的测试指令:

1from selenium import webdriver
2browser = webdriver.Chrome()
3browser.get('https://blog.361way.com')

该操作需要在图形界面下进行,浏览器会自动打开并访问网页。接下来说没有图形化,在全终端下的操作headless模式:

1from selenium import webdriver
2chrome_options = webdriver.ChromeOptions()
3chrome_options.add_argument('--no-sandbox')
4chrome_options.add_argument('--headless')
5chrome_options.add_argument('--disable-gpu')
6browser = webdriver.Chrome(options=chrome_options)
7browser.get('https://blog.361way.com')
8data = browser.page_source

page_souce属性可以获取html网页源码。

四、元素查找和获取

使用上面的方法,我在抓取某财经页面的json信息时,发现一个通过浏览器直接打开是全json的页面,结果使用该模示会输出一些html头。如下:

chrome-headless
chrome-headless

出现该情况,可以使用find_element_by_tag_name进行处理,这里给个示例代码如下:

 1#!/usr/bin/env python
 2# coding=utf8
 3#import sys
 4#reload(sys)
 5#sys.setdefaultencoding('utf8')
 6import json
 7from selenium import webdriver
 8chrome_options = webdriver.ChromeOptions()
 9chrome_options.add_argument('--no-sandbox')
10chrome_options.add_argument('--headless')
11chrome_options.add_argument('--disable-gpu')
12browser = webdriver.Chrome(options=chrome_options)
13browser.get('http://www.iwencai.com/stockpick/load-data?typed=0&preParams=&ts=1&f=1&qs=result_original&selfsectsn=&querytype=stock&searchfilter=&tid=stockpick&w=白酒&queryarea=')
14#data = browser.page_source
15#print data
16pre = browser.find_element_by_tag_name("pre").text
17d = json.loads(pre,encoding='utf-8')
18#print(data)
19result = d['data']['result']['result']
20import datetime
21today = datetime.date.today()
22fname = today.strftime("%Y%m%d")
23#print(result[0][:2])
24f= open(fname, 'w')
25for data in result:
26    print(data[:2])
27    txt =  data[0] + '  ' + data[1] + '\n'
28    #f.write(str(data[:2]))
29    f.write(txt)
30f.close( )

Headless Chrome玩法还有很多,比如关于websoket类型的数据的获取、js dom类型的数据获取、和其他常见模块及语言的整合等。这个后面慢慢的再理一些东西出来。