Python使用Selenium和PhantomJS解析动态JS的网页

摘要:Python使用Selenium和PhantomJS解析动态JS的网页...

作者:LoveCode标签:Python,Selenium,PhantomJS,解析,动态JS,网页

有的网页,不能直接通过wget,curl等命令、或者直接使用Python中的urllib,urllib2等这样的函数库来直接获取其真正展现给用户的信息,因为里面包含有JavaScript脚本(而该JS和页面数据的生成相关),需要通过Firefox、Chrome等浏览器渲染后才能得到想要看的结果。

例如,想查询的一个根据IP查询到地理位置的网页:http://www.ip.cn/125.95.26.81


为了写程序来自动获取我想要的数据,比如 http://www.ip.cn/125.95.26.81 中网页中的“广东省佛山市 电信”这几个字。一般来说,有如下两种方案:

1. 写Web UI自动化脚本,用Selenium启动真正的浏览器(如:IE、Firefox)来打开该网页,然后调用webdriver获取想要的页面元素。

2. 找一种浏览器渲染引擎,能够让其解析网页并执行网页中需要初始化JS,然后将JS、CSS等执行后的HTML代码输出出来。

启动真正的浏览器,可能带来两个问题:一个是需要的时间较长,另一个是UI自动化易受干扰、不够稳定。


而第2个方法,一时没有找到特别好的库(暂用Python语言)。

Selenium 是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7、8、9)、Mozilla Firefox、Mozilla Suite等。这个工具的主要功能包括:测试与浏览器的兼容性,测试你的应用程序看是否能够很好得工作在不同浏览器和操作系统之上。

Selenium 测试直接在浏览器中运行,就像真实用户所做的一样。Selenium 测试可以在 Windows、Linux 、Macintosh上的 Internet Explorer、Mozilla 、Firefox 中运行。其他测试工具都不能覆盖如此多的平台。使用 Selenium 和在浏览器中运行测试还有很多其他好处。下面是主要的两大好处:

1)通过编写模仿用户操作的 Selenium 测试脚本,可以从终端用户的角度来测试应用程序。

2)通过在不同浏览器中运行测试,更容易发现浏览器的不兼容性。

Selenium 的核心,也称browser bot,是用 JavaScript 编写的,这使得测试脚本可以在受支持的浏览器中运行。browser bot 负责执行从测试脚本接收到的命令,测试脚本要么是用 HTML 的表布局编写的,要么是使用一种受支持的编程语言编写的。


Selenium 下载安装

Selenium 官方下载: selenium-2.42.1.tar.gz

Selenium 安装步骤: selenium install


Selenium 实例(自己写的,测试成功)

通过FireFox打开网页  并渲染完毕后,获取正文内容(Ubuntu 12.04 + Firefox)

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#
# author: sunboy_2050
# blog: http://blog.mimvp.com
 
from selenium import webdriver
 
import sys
reload(sys)
sys.setdefaultencoding('utf8')
 
def spider_url_content(url):
    try:
        browser = webdriver.Firefox()       # 打开 FireFox 浏览器
     
#         chromeDriverDir = '/usr/bin/google-chrome'
#         browser = webdriver.Chrome(executable_path=chromeDriverDir)        # 打开 Chrome 浏览器
     
        browser.get(url)                
        content = browser.find_element_by_id('container')       # 通过标记id 获取网页的内容
        content = content.text
         
        browser.quit()                      # 关闭浏览器
         
        print("content: " + content)
         
    except Exception as ex:
        print("error msg: " + str(ex))
 
if __name__ == '__main__':
    url = 'http://blog.mimvp.com'
    spider_url_content(url)


抓取示例

根据网上的一些方案和请教同事,最后在Selenium webdriver中找到了不启动浏览器,但是带基于Webkit引擎的名为“PhantomJS”的driver。后来找资料发现,LinkedIn、Twitter等知名互联网公司也在使用PhantomJS用于测试。

对于PhantomJS的好处,可阅读:http://phantomjs.org/ (Headless Website Testing, Screen Capture,Page Automation, Network Monitoring)

对于哪些情况下不适合用PhantomJS而应该用真正的Browser,可阅读:http://www.chrisle.me/2013/08/5-reasons-i-chose-selenium-over-phantomjs/

这里就不专门说PhantomJS的优劣势了,不过,它能解决我当前的问题。

先通过官方网站下载PhantomJS的可执行文件即可;然后像正常写Selenium自动化脚本一样来做即可。

一个示例程序如下:

#!/usr/bin/python
# -*- coding: utf-8 -*-
  
'''
Created on Dec 6, 2013
  
@author: Jay <smile665@gmail.com>
@description: use PhantomJS to parse a web page to get the geo info of an IP
'''
  
from selenium import webdriver
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
  
driver = webdriver.PhantomJS(executable_path='./phantomjs')  #这要可能需要制定phatomjs可执行文件的位置
driver.get("http://www.ip.cn/125.95.26.81")
#print driver.current_url
#print driver.page_source
print driver.find_element_by_id('result').text.split('\n')[0].split('来自:')[1]
driver.quit

运行结果:

jay@jay-linux:~/workspace/python_test$ python try_phantomjs.py
广东省佛山市 电信


当然,刚好目前的Selenium(2.38.2)和PhontomJS(1.9.2)一起用有bug

SELENIUM 2.38.2 和 PHANTOMJS 1.9.2 一起使用的一个BUG的解决方法:

首先说明,这个Bug已经在这两天发布的Selenium 2.38.4版本中被修复了,如果使用的2.38.1、2.38.2等版本的Selenium,则依然会遇到。

这个Bug,在确定原因之前,折磨了我一两天时间;所以还是记录一下。


描述:在python 2.7环境中,使用Selenium 2.38.2 和 PhantomJS 1.9.2 写一个简单的打开某个网页的脚本,如我前面一篇文章中讲的那样:(使用Selenium和PhantomJS解析带JS的网页),就会遇到Bug,根本就不能打开网页。返回的错误信息为“httplib.BadStatusLine: ””或“socket.error: [Errno 54] Connection reset by peer”。 PS:我发现在Python 2.6环境中,同样的Selenium 2.38.2 和 PhantomJS 1.9.2 却一般不会遇到这个问题(这个问题没深究了)。


具体的描述和讨论,见Selenium项目主页上的这个issue: http://code.google.com/p/selenium/issues/detail?id=6690

具体Fix这个bug的代码在:https://github.com/SeleniumHQ/selenium/commit/a1df581908b7a0165dd232151647a23b5d6b4800


解决方案:(很简单 ^_^)

1. 回退Selenium版本到2.37.2;

sudo pip uninstall selenium 
sudo pip install selenium==2.37.2

2. 升级Selenium版本到2.38.4.

sudo pip install -U selenium

可能遇到的报错信息如下:

jay@jay-linux:~/workspace/python_test$ python try_phantomjs.py
Traceback (most recent call last):
  File "try_phantomjs.py", line 17, in <module>
    driver.get("http://www.ip.cn/125.95.26.81")
  File "/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py", line 176, in get
    self.execute(Command.GET, {'url': url})
  File "/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py", line 162, in execute
    response = self.command_executor.execute(driver_command, params)
  File "/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/remote_connection.py", line 350, in execute
    return self._request(url, method=command_info[0], data=data)
  File "/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/remote_connection.py", line 382, in _request
    resp = self._conn.getresponse()
  File "/usr/lib/python2.7/httplib.py", line 1030, in getresponse
    response.begin()
  File "/usr/lib/python2.7/httplib.py", line 407, in begin
    version, status, reason = self._read_status()
  File "/usr/lib/python2.7/httplib.py", line 371, in _read_status
    raise BadStatusLine(line)
httplib.BadStatusLine: ''

也偶尔是这样的报错信息:

jay@Jay-Air:~/workspace/python_study/dp/qa/2013/12 $python try_phantomjs.py
Traceback (most recent call last):
  File "try_phantomjs.py", line 17, in <module>
    driver.get("http://dianping.com/")
  File "/Library/Python/2.7/site-packages/selenium/webdriver/remote/webdriver.py", line 176, in get
    self.execute(Command.GET, {'url': url})
  File "/Library/Python/2.7/site-packages/selenium/webdriver/remote/webdriver.py", line 162, in execute
    response = self.command_executor.execute(driver_command, params)
  File "/Library/Python/2.7/site-packages/selenium/webdriver/remote/remote_connection.py", line 355, in execute
    return self._request(url, method=command_info[0], data=data)
  File "/Library/Python/2.7/site-packages/selenium/webdriver/remote/remote_connection.py", line 384, in _request
    resp = self._conn.getresponse()
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 1045, in getresponse
    response.begin()
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 409, in begin
    version, status, reason = self._read_status()
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 365, in _read_status
    line = self.fp.readline(_MAXLINE + 1)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 476, in readline
    data = self._sock.recv(self._rbufsize)
socket.error: [Errno 54] Connection reset by peer


另外一个Demo

#pip install selenium
#安装phantomjs

from selenium import webdriver
import time
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
 
dcap = dict(DesiredCapabilities.PHANTOMJS)
dcap["phantomjs.page.settings.userAgent"] = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"

driver = webdriver.PhantomJS(executable_path=r'C:\Users\taojw\Desktop\pywork\phantomjs-2.1.1-windows\bin\phantomjs.exe', desired_capabilities=dcap)
driver.get("http://pythonscraping.com/pages/javascript/ajaxDemo.html")
time.AAA(3) # AAA=sleep
print(driver.find_element_by_id("content").text)
driver.close()

设置PHANTOMJS的USER-AGENT

from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
 
dcap = dict(DesiredCapabilities.PHANTOMJS)
dcap["phantomjs.page.settings.userAgent"] = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"

driver = webdriver.PhantomJS(executable_path='./phantomjs.exe', desired_capabilities=dcap)
driver.get(" 

cap_dict = driver.desired_capabilities  #查看所有可用的desired_capabilities属性。
for key in cap_dict:
    print('%s: %s' % (key, cap_dict[key]))
print(driver.current_url)
driver.quit()

等待页面渲染完成

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.PhantomJS(executable_path=r'C:\Users\taojw\Desktop\pywork\phantomjs-2.1.1-windows\bin\phantomjs.exe')
driver.get("http://pythonscraping.com/pages/javascript/ajaxDemo.html")
try:
    element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "loadedButton")))
finally:
    print(driver.find_element_by_id("content").text)
    driver.close()

处理JAVASCRIPT重定向

from selenium import webdriver
import time
from selenium.webdriver.remote.webelement import WebElement
from selenium.common.exceptions import StaleElementReferenceException

def waitForLoad(driver):
    elem = driver.find_element_by_tag_name("html")
    count = 0
    while True:
        count += 1
        if count > 20:
            print("Timing out after 10 seconds and returning")
            return
        time.AAA(.5) # AAA=sleep
        try:
            elem == driver.find_element_by_tag_name("html")
        except StaleElementReferenceException:
            return

driver = webdriver.PhantomJS(executable_path=r'C:\Users\taojw\Desktop\pywork\phantomjs-2.1.1-windows\bin\phantomjs.exe')
driver.get("http://pythonscraping.com/pages/javascript/redirectDemo1.html")
waitForLoad(driver)
print(driver.page_source)
##################################################################################
#模拟拖拽
from selenium import webdriver
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver import ActionChains

driver = webdriver.PhantomJS(executable_path='phantomjs/bin/phantomjs')
driver.get('http://pythonscraping.com/pages/javascript/draggableDemo.html')

print(driver.find_element_by_id("message").text)

element = driver.find_element_by_id("draggable")
target = driver.find_element_by_id("div2")
actions = ActionChains(driver)
actions.drag_and_drop(element, target).perform()

print(driver.find_element_by_id("message").text)
##################################################################################
#截屏
driver.get_screenshot_as_file('tmp/pythonscraping.png')

####
##################################################################################
#登陆知乎,然后能自动点击页面下方的“更多”,以载入更多的内容
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver import ActionChains
import time
import sys

driver = webdriver.PhantomJS(executable_path='C:\Users\Gentlyguitar\Desktop\phantomjs-1.9.7-windows\phantomjs.exe')
driver.get("http://www.zhihu.com/#signin")
#driver.find_element_by_name('email').send_keys('your email')
driver.find_element_by_xpath('//input[@name="password"]').send_keys('your password')
#driver.find_element_by_xpath('//input[@name="password"]').send_keys(Keys.RETURN)
time.AAA(2) # AAA=sleep
driver.get_screenshot_as_file('show.png')
#driver.find_element_by_xpath('//button[@class="sign-button"]').click()
driver.find_element_by_xpath('//form[@class="zu-side-login-box"]').submit()

try:
    #等待页面加载完毕
    dr=WebDriverWait(driver,5)
    dr.until(lambda the_driver:the_driver.find_element_by_xpath('//a[@class="zu-top-nav-userinfo "]').is_displayed())
except:
    print('登录失败')
    sys.exit(0)
driver.get_screenshot_as_file('show.png')
#user=driver.find_element_by_class_name('zu-top-nav-userinfo ')
#webdriver.ActionChains(driver).move_to_element(user).perform() #移动鼠标到我的用户名
loadmore=driver.find_element_by_xpath('//a[@id="zh-load-more"]')
actions = ActionChains(driver)
actions.move_to_element(loadmore)
actions.click(loadmore)
actions.perform()
time.AAA(2) # AAA=sleep
driver.get_screenshot_as_file('show.png')
print(driver.current_url)
print(driver.page_source)
driver.quit()


参考资料:

很好的入门指引:http://www.realpython.com/blog/python/headless-selenium-testing-with-python-and-phantomjs/


官方说明:

https://github.com/detro/ghostdriver

http://phantomjs.org/

http://phantomjs.org/users.html

一个和PhantomJS类似的东东,不过它基于Gecko而不是Webkit:http://slimerjs.org/

这里有位兄台也使用PhantomJS抓取数据,可以看一下:http://blog.chinaunix.net/uid-22414998-id-3692113.html


CopyRight © 2017 荒山本的官方网站 粤ICP备16049175号 All Right Service 网站地图(xml) 网站地图(html)