使用python比较多的一个应用场景就是数据采集,采集一些比较蛋疼的页面时,会检测用户是否是通过浏览器打开的页面,还有一些会通过js加载后才会回显内容。这个时候可能用的比较多的方案就是python + Headless + 浏览器,其会自动打开浏览器,并输入相应的页面地址后,并可以抓取返回的结果。不过在linux上经常是没有GUI图形化下跑的,之前有一个开源项目叫PhantomJS ,不过目前该项目已停止了。原因很简单,chrome59版本后加入了 Headless Chrome。Headless Chrome 是 Chrome 浏览器的无界面形态,可以在不打开浏览器的前提下,使用所有Chrome支持的特性,在命令行中运行你的脚本。相对于传统的chrome浏览器,这是一个可以在后台用命令行操作浏览器的工具,对于爬虫编写以及web自动化测试都有很大的作用。相比较同类工具Phantomjs,其更加强大(主要因为其依赖的webkit更新),这也是Phantomjs暂停开发的原因。
一、chrome的安装
这里以centos7为例,直接使用官方源
# vim /etc/yum.repos.d/chrome.repo
写入以下内容:
[google-chrome]
name=google-chrome
baseurl=http://dl.google.com/linux/chrome/rpm/stable/$basearch
enabled=1
gpgcheck=0
gpgkey=https://dl-ssl.google.com/linux/linux_signing_key.pub
配置完成后,使用如下命令进行安装:yum -y install google-chrome-stable --nogpgcheck
二、Headless Chrome的简单使用
先说下不和selenium或其他python模块配合使用的情况下,该模式的一些简单使用。
# 输出html:
google-chrome --headless --dump-dom http://www.361way.com
# 将目标页面截图:
google-chrome --headless --disable-gpu --screenshot http://www.361way.com
# 规定大小
google-chrome --headless --disable-gpu --screenshot --window-size=640,960 http://www.361way.com
# 保存为pdf:
google-chrome --headless --disable-gpu --print-to-pdf http://www.361way.com
这里使用https地址也是一样的。上面的命令在有些版本下会要求加入--no-sandbox参数,这个可以根据报错信息进行调整。
除了上面的提到的,还有远程调用模式--remote-debugging-port,个人理解这个有点类型于代理映射模式。通过把远程的一个请求映射到本地的一个端口上,方便进行调试管理。使用如下:
google-chrome --headless --disable-gpu --no-sandbox --remote-debugging-port=9222 --user-data-dir='/d/site' http://www.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、国内镜像。这里下载的时候注意下载的驱动插件要和浏览器相应的版本相对应。
来一个打开本站点的测试指令:
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.361way.com')
该操作需要在图形界面下进行,浏览器会自动打开并访问网页。接下来说没有图形化,在全终端下的操作headless模式:
from selenium import webdriver
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
browser = webdriver.Chrome(options=chrome_options)
browser.get('https://www.361way.com')
data = browser.page_source
page_souce属性可以获取html网页源码。
四、元素查找和获取
使用上面的方法,我在抓取某财经页面的json信息时,发现一个通过浏览器直接打开是全json的页面,结果使用该模示会输出一些html头。如下:
出现该情况,可以使用find_element_by_tag_name进行处理,这里给个示例代码如下:
#!/usr/bin/env python
# coding=utf8
#import sys
#reload(sys)
#sys.setdefaultencoding('utf8')
import json
from selenium import webdriver
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
browser = webdriver.Chrome(options=chrome_options)
browser.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=')
#data = browser.page_source
#print data
pre = browser.find_element_by_tag_name("pre").text
d = json.loads(pre,encoding='utf-8')
#print(data)
result = d['data']['result']['result']
import datetime
today = datetime.date.today()
fname = today.strftime("%Y%m%d")
#print(result[0][:2])
f= open(fname, 'w')
for data in result:
print(data[:2])
txt = data[0] + ' ' + data[1] + '\n'
#f.write(str(data[:2]))
f.write(txt)
f.close( )
Headless Chrome玩法还有很多,比如关于websoket类型的数据的获取、js dom类型的数据获取、和其他常见模块及语言的整合等。这个后面慢慢的再理一些东西出来。