Open jlhnihao opened 8 years ago
谢谢你提出了一个非常有价值的问题。
我看了一下,腾讯新闻的网页把评论内容和控制按钮都放在了一个 id 为 commentIframe 的 iFrame 里,所以直接用
> loadMoreButton <- element_xpath_find(value = '//*[@id="loadMore"]/span')
这样的语句是找不到那个“加载更多”按钮的,只会得到一个空对象,再用
> element_click(loadMoreButton)
点击不到那个按钮,也就不会有点击的效果。
其实这种情况在微博里也存在。你提的这个问题对于国内很多网站有代表性,而原书中并没有分析这种问题的解决办法。
顺带说一句,其实 iFrame 有很多弊端,W3C 早就不推荐使用了,不理解为何国内这么多网站还在用,可能是因为它们用的某些框架或者库内部就是这样实现的吧。
按照 9.1.9 节讲解的方法确实解决不了这个问题。里面用到的 Rwebdriver 组件是该书的作者之一 C. Rubba 编写的,我个人觉得它做得并不好,在它的文档里也没找到如何处理 iFrame 的功能。后来我改用 RSelenium 组件,很轻松地解决了 iFrame 问题。它的文档可以参阅:https://cran.r-project.org/web/packages/RSelenium/vignettes/RSelenium-basics.html 。
解决这个问题的总体思路就是,先找到 “加载更多” 评论那个按钮所在的 iFrame,然后把当前 DOM 切换到这个 iFrame,再从中去定位该按钮。获得这个按钮之后,可以用一个 while 循环重复点击动作,直到所有评论加载完成、按钮失效为止,这种情况下就说明所有评论都已成功加载到 DOM 里了。
我自己尝试的代码是这样的:
> install.packages("RSelenium")
> library(RSelenium)
> library(XML)
> remDr <- remoteDriver(remoteServerAddr = "localhost" , port = 4444, browserName = "firefox" )
> remDr$open()
> remDr$navigate('http://coral.qq.com/1398070563/')
> iFrame <- remDr$findElement(using = 'xpath', "//*[@id = 'commentIframe']") # 评论所在的 iFrame
> remDr$switchToFrame(iFrame) # 先切换到评论 iFrame
> loadMoreButton <- remDr$findElement(using = 'xpath', "//*[@id = 'loadMore']/span") # 在评论 iFrame 里找到那个 “加载更多” 按钮
> while(exists("loadMoreButton")) {loadMoreButton$clickElement() ; loadMoreButton <- remDr$findElement(using = 'xpath', "//*[@id = 'loadMore']/span") } # 在评论 iFrame 里循环点击 “加载更多” 按钮,直到评论加载完该按钮不再出现为止(需要等待的时间比较长)
> comments <- remDr$findElement(using = 'xpath', '//*[@id="allComments"]') # 获取的所有评论就在 comments 里。这是一个大 div ,评论信息还需要进一步提取,这个就留给你继续吧
以上代码我在自己的环境下测试通过(MacBook Pro, OSX 10.11.4,JDK 8, Selenium 2.53.0 , R 3.3.0, IDE 是 RStudio),你可以试试,有问题再告诉我。
============================= 高级班的分隔线 ==================================
其实这个问题对于掌握了 JavaScript 语言的读者来说就非常简单了,因为 JS 可以很方便地操作 DOM,触发事件。
这里的思路和上面是一样的,但是不需要任何库、插件、组件之类,连 R 语言和 Selenium 都不需要,只要有个浏览器即可,简单得不能再简单了。以下代码可以在加载了这个新闻的页面上右键点击“检查元素”(Inspect Element),在“控制台”(Console )中输入并执行:
var iframe = document.getElementById('commentIframe'); // 找到评论所在的 iFrame
var innerDoc = iframe.contentDocument || iframe.contentWindow.document; // 定位到 iFrame 的DOM
var event = document.createEvent("HTMLEvents");
event.initEvent("click", false, false); // 给这个按钮初始化一个点击事件
var allComments, t, loadMore;
function readComments(){
loadMore = innerDoc.getElementById('loadMore').children[0]; // 等待 DOM 加载完成,重新读取这个按钮
if(loadMore.nextElementSibling.style.display != 'block') { // 隐藏的“没有更多评论” 提示文字不显示,说明评论尚未全部加载
loadMore.dispatchEvent(event); // 触发点击事件
}
else{
clearInterval(t); // 按钮已经不显示,说明加载完成,可以清除循环
console.log('Done!'); // 在控制台显示任务完成的提示
allComments = innerDoc.getElementById('allComments'); // 所有评论都在这里了
}
}
t = setInterval("readComments()",10000); // 每次点击后先等待 10 秒,让新评论加载进来。
执行完这些代码后,所有的评论都在 allComments 对象里,后续就可以对它进行分解和分析。
正如我在译者序里所说的,本书用 R 语言作为代码演示的平台,但是书中讲解的方法并不局限于 R 语言,在很多语言平台上都是很容易实现的。读者也可以用自己熟悉的语言来尝试一下,这样才是学以致用嘛。
谢谢您抽出宝贵的时间来解答,这几天因忙于它事未能及时回复,还请海涵。 循着您介绍的思路,把网页Frame、iframe,以及RSelenium包的介绍读了几遍,很受益。读完自己先尝试做了一下,最后回头看了您提供的代码。 前面写的跟您提供的代码类似,只是while后面有点不同,但基本思路是跟您一致的。我贴在下面:
while(loadMoreButton$getElementText()[[1]]=="加载更多") {
loadMoreButton$clickElement()
loadMoreButton <- remDr$findElement(using = 'xpath', "//*[@id = 'loadMore']/span")
}
iframe_parsed<-htmlParse(remDr$getPageSource()[[1]],encoding="UTF-8")
iframe_value<-xpathSApply(iframe_parsed,"//li//div[@class='np-post-content']/p",xmlValue)
因为发现点击“加载更多”点到最后是“没有更多了”,但该span仍然存在,也就是exists("loadMoreButton")依然是TRUE,这样就会继续往下循环,系统会报错:“Summary: UnknownError...”,但仍能正常出结果。所以这个地方修改了一下。 后面两句解析后,运用本书前面所讲的内容,就不存在从大div里面再进行提取数据的操作了。 我的运行环境是win7系统下R Console(64-bit 3.3.0),基本上就在里面直接写了。 您讲的运用JavaScript,对我启发也很大(虽然还没学过,有时间补上这一块。) 再次感谢您指点。
老师好,我是一名学生,正在学习您的书,上面您的代码中remDr <- remoteDriver(remoteServerAddr = "localhost" , port = 4444, browserName = "firefox" )
我在WINDOWS系统上操作,运行remDr$open()
就出错:Error in queryRD(paste0(serverURL, "/session"), "POST", qdata = toJSON(serverOpts)) :
,纠结了一天,试了好些地方没找到原因,希望两位老师解答一下,谢谢老师!
@jlhnihao @coderLMN
这个错误很有可能是因为你没有成功启动 Selenium ,可以看一下 Selenium 启动界面里是否有错误信息。
出错了要看错误信息啊:
Error: Unable to access jarfile selenium-server-standalone-2.53.0.jar
这个信息说的就是访问不到你的 Selenium jar 文件。从你的命令看:
C:>java -jar selenium-server-standalone-2.53.0.jar
所以 java 会在你的 C 盘当前目录下找这个 jar 文件,如果这个文件不在那里,那你就要给出具体的路径才行。
@老师,前面那个错误变成下面这样,好像就是上面jlhnihao 说的 `> remDr<-remoteDriver(remoteServerAddr = "localhost" , port = 4444, browserName = "firefox",platform="WINDOWS")
remDr$open() [1] "Connecting to remote server" Error:Summary: UnknownError Detail: An unknown server-side error occurred while processing the command. class: org.openqa.selenium.WebDriverException` jlhnihao 说的方式我也试了,改了改也不行!selenium也启动了。再次来请教您!谢谢您的耐心指导! @coderLMN @jlhnihao
这个错误应该是 Selenium 服务器无法启动 firefox 浏览器,有可能有两个原因:1. 有其它程序占用了 4444 端口,或者 java 的版本太低,导致你的 Selenium 服务器并没有正常启动。可以去看一下启动的日志是否有报错; 2. firefox 浏览器的路径没有保存到系统路径里,所以找不到。可以试试重新安装 firefox 。
老师,我Java已经是最新的,Firefox又重新安装到系统路径,可还是不行!
> remDr$open() [1] "Connecting to remote server" Error: Summary: UnknownError Detail: An unknown server-side error occurred while processing the command. class: java.lang.IllegalArgumentException
是不是系统不行啊!我的是WINDOWS 10
remDr<-remoteDriver(remoteServerAddr="localhost",port = 4444,browserName="Firefox",platform="WINDOWS 10")
你能把 Selenium 启动时输出的内容贴过来吗?我怀疑还是 Selenium 的启动有问题。
这是在系统命令行运行的结果,老师您看一下!
C:\Users\zhang>java -jar selenium-server-standalone-2.53.0.jar 13:58:31.340 INFO - Launching a standalone Selenium Server 13:58:31.447 INFO - Java: Oracle Corporation 25.65-b01 13:58:31.449 INFO - OS: Windows 10 10.0 amd64 13:58:31.479 INFO - v2.53.0, with Core v2.53.0. Built from revision 35ae25b 13:58:31.568 INFO - Driver class not found: com.opera.core.systems.OperaDriver 13:58:31.571 INFO - Driver provider com.opera.core.systems.OperaDriver is not registered 13:58:31.589 INFO - Driver provider org.openqa.selenium.safari.SafariDriver registration is skipped: registration capabilities Capabilities [{browserName=safari, version=, platform=MAC}] does not match the current platform WIN10 13:58:31.598 INFO - Driver class not found: org.openqa.selenium.htmlunit.HtmlUnitDriver 13:58:31.604 INFO - Driver provider org.openqa.selenium.htmlunit.HtmlUnitDriver is not registered 13:58:31.763 INFO - RemoteWebDriver instances should connect to: http://127.0.0.1:4444/wd/hub 13:58:31.766 INFO - Selenium Server is up and running
在R里运行前面两行就出现下面的错误: `> remDr<-remoteDriver(remoteServerAddr="localhost",port = 4444,browserName="Firefox",platform="WINDOWS 10")
remDr$open() [1] "Connecting to remote server" Error: Summary: UnknownError Detail: An unknown server-side error occurred while processing the command. class: java.lang.IllegalArgumentException`
我手头没有 windows 10 的电脑,没法重现。不过,
platform="WINDOWS 10"
这个参数不是必需的,也不一定正确,可以去掉,再试试看。
老师,好像那个参数不重要,去掉也不行,系统命令行我选了一些信息,您看一下!
Caused by: org.openqa.selenium.WebDriverException: The best matching driver provider org.openqa.selenium.ie.InternetExplorerDriver can't create a new driver instance for Capabilities [{nativeEvents=true, browserName=Firefox, javascriptEnabled=true, version=, platform=ANY}]
java.util.concurrent.ExecutionException: org.openqa.selenium.WebDriverException: The best matching driver provider org.openqa.selenium.ie.InternetExplorerDriver can't create a new driver instance for Capabilities [{nativeEvents=true, browserName=Firefox, javascriptEnabled=true, version=, platform=ANY}] Build info: version: '2.53.0', revision: '35ae25b', time: '2016-03-15 17:00:58' System info: host: 'zhang-PC', ip: '192.168.0.106', os.name: 'Windows 10', os.arch: 'amd64', os.version: '10.0', java.version: '1.8.0_65' Driver info: driver.version: unknown
我已经重现了你这个问题。
在你的命令
> remDr<-remoteDriver(remoteServerAddr="localhost",port = 4444,browserName="Firefox")
里面,browserName="Firefox" 里的 firefox 第一个 f 不能是大写,否则就会出这个错误:
Error: Summary: UnknownError Detail: An unknown server-side error occurred while processing the command. class: org.openqa.selenium.WebDriverException
你把 F 改成小写再试试。
@coderLMN @KAIKAIZHANG 你们好, 我与@KAIKAIZHANG 遇到的情况类似----没有成功启动chrome浏览器,不知道哪里没设置好 以下是使用的过程反馈: Selenium 版本:selenium-server-standalone-2.53.0.jar; W7系统;电脑已安装chrome浏览器;java版本7.0.650 第一步打开Selenium
第二步:回到R的控制台执行代码:
library(RSelenium) library(XML) remDr <- remoteDriver(remoteServerAddr = "localhost" , port = 4444, browserName = "chrome",platform="WINDOWS" ) remDr$open() [1] "Connecting to remote server" Error: Summary: UnknownError Detail: An unknown server-side error occurred while processing the command. class: java.lang.IllegalStateException
Selenium执行结果:
@LeeWill2016 ,stackoverflow这里有2个贴子,跟启用Chrome有关的,可以看一下: http://stackoverflow.com/questions/33124857/rselenium-is-not-working http://stackoverflow.com/questions/31124702/rselenium-unknownerror-java-lang-illegalstateexception-with-google-chrome
如果需要启用Chrome,需要先下载ChromeDriver.exe, 然后在环境变量中设置这个文件的路径。
@coderLMN 您好,在运行RSelenium的使用报错,google了几天,仍然没有解决,希望指点一下:
require(RSelenium)
# 切换selenium-server-standalone.jar 的路径
setwd("D:\\Program Files\\R\\R-3.3.1\\library\\RSelenium\\bin")
startServer()
remDr <- remoteDriver(remoteServerAddr = "localhost"
, port = 4444
, browserName = "firefox"
)
remDr$open()
# 运行remDr$open()报错
[1] "Connecting to remote server"
Error: Summary: UnknownError
Detail: An unknown server-side error occurred while processing the command.
class: org.openqa.selenium.WebDriverException
# R的基本信息如下,且JAVA也已经更新到最新的版本
platform arch os
"x86_64-w64-mingw32" "x86_64" "mingw32"
system status major
"x86_64, mingw32" "" "3"
minor year month
"3.1" "2016" "06"
day svn rev language
"21" "70800" "R"
version.string nickname
"R version 3.3.1 (2016-06-21)" "Bug in Your Hair"
@LeeWill2016 : @urdaddy85 说得对,缺省支持的是 firefox,其他浏览器需要安装驱动。
@urdaddy85 你的操作系统 mingw32 我不了解,会不会是因为这个操作系统的问题?你是否可以换个其他操作系统试试看?
@coderLMN 感谢回复。我用的是WIN10的操作系统。最终用了PhantomJS去链接服务器。
问题解决:
您好,我从9.1.9节看到从实时DOM树中提取底层的HTML代码,于是想在本节的框架下提取腾讯新闻的评论,但好像page_source()函数并不获得含有评论内容的DOM树。在其他章节我没找到如何解决该问题的线索,特向您请教。如果需要的话,不如我们就以最近闹得沸沸扬扬的雷洋的新闻事件的评论为例,我附上评论网址:http://coral.qq.com/1398070563 。希望能得到您的帮助,非常感谢。