RSelenium+foreach并行爬取Oddsportal重复结果及登录问题求助
解决RSelenium并行爬取的重复结果与登录后爬取问题
一、并行爬取重复结果的原因与修复
问题根源
你当前的代码里,所有集群节点都硬编码了同一个port=8910,这意味着所有worker都连接到同一个PhantomJS实例。当foreach循环执行时,多个worker会同时对这个单一浏览器实例执行navigate操作,最终获取的是最后一次导航完成的页面标题,所以会出现结果重复3次的情况(对应你开启的3个核心节点)。
修复方案
每个集群节点需要启动独立的浏览器实例,不要指定固定端口,让RSelenium自动分配可用端口。修改后的核心代码如下:
library(RSelenium) library(rvest) library(magrittr) library(foreach) library(doParallel) URLsPar <- c("http://www.bbc.com/", "http://www.cnn.com", "http://www.google.com", "http://www.yahoo.com", "http://www.twitter.com", "http://www.oddsportal.com") # 初始化集群,预留1个核心给系统 num_cores <- detectCores() - 1 cl <- makeCluster(num_cores) registerDoParallel(cl) # 每个worker启动独立的浏览器实例,不固定端口 clusterEvalQ(cl, { library(RSelenium) eCap <- list(phantomjs.page.settings.userAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20120101 Firefox/29.0", phantomjs.page.settings.loadImages = FALSE, phantomjs.phantom.cookiesEnabled = FALSE, phantomjs.phantom.javascriptEnabled = TRUE) # 不指定port,让RSelenium自动分配可用端口 remDr <- remoteDriver(browserName = "phantomjs", extraCapabilities = eCap) remDr$open() }) # 并行爬取,每个worker操作自己的浏览器实例 strt <- Sys.time() ws <- foreach(x = seq_along(URLsPar), .packages = c("rvest", "magrittr", "RSelenium")) %dopar% { remDr$navigate(URLsPar[x]) Sys.sleep(1) # 短延迟确保页面加载完成 remDr$getTitle()[[1]] } end <- Sys.time() - strt # 修正时间计算逻辑 # 关闭每个worker的浏览器并停止集群 clusterEvalQ(cl, { remDr$close() }) stopCluster(cl) # 查看结果和耗时 print(ws) print(end)
关键改进点
- 移除硬编码端口,让每个worker启动独立的PhantomJS实例,避免操作冲突
- 修正时间计算错误(原代码
strt-Sys.time()会得到负数) - 添加
Sys.sleep(1)确保页面加载完成,避免因页面未渲染完全获取不到标题 - 使用
seq_along(URLsPar)代替固定的1:6,让代码适配任意数量的URL
二、登录后爬取返回NULL的问题解决
问题分析
登录后返回NULL通常有几个原因:
- 登录操作未完成就执行后续爬取(页面加载慢,元素还未渲染)
- 登录时关闭了cookie功能,导致会话无法保持
- 遇到网站反爬机制,登录请求被拦截
修复方案
在每个worker的初始化流程中完成登录,确保cookie启用,并添加足够的等待时间保证操作完成:
修改后的clusterEvalQ登录部分代码:
clusterEvalQ(cl, { library(RSelenium) eCap <- list(phantomjs.page.settings.userAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20120101 Firefox/29.0", phantomjs.page.settings.loadImages = FALSE, phantomjs.phantom.cookiesEnabled = TRUE, # 登录需要保存cookie,设为TRUE phantomjs.phantom.javascriptEnabled = TRUE) remDr <- remoteDriver(browserName = "phantomjs", extraCapabilities = eCap) remDr$open() # 执行登录流程,添加等待确保元素加载 remDr$navigate("https://www.oddsportal.com/login/") Sys.sleep(2) # 等待登录页面加载完成 # 输入账号信息(替换为你的实际账号) username_elem <- remDr$findElement(using = "id", value = "login-username") username_elem$sendKeysToElement(list("your_username")) password_elem <- remDr$findElement(using = "id", value = "login-password") password_elem$sendKeysToElement(list("your_password")) # 点击登录按钮 login_btn <- remDr$findElement(using = "css selector", value = "button[type='submit']") login_btn$clickElement() Sys.sleep(3) # 等待登录跳转,确保会话已建立 })
关键注意事项
- 登录必须启用cookie,否则无法保持登录状态
- 添加足够的
Sys.sleep等待页面加载和操作完成,避免因元素未找到导致失败 - 如果网站有验证码或其他反爬机制,需要额外处理(比如手动输入验证码,或使用验证码识别工具)
- 每个worker的登录状态是独立的,所以每个worker都需要完成完整的登录流程
额外优化建议
- 替换PhantomJS为无头Chrome/Firefox:PhantomJS已停止维护,无头Chrome/Firefox稳定性更好,示例配置如下:
# 无头Chrome配置 eCap <- list(chromeOptions = list( args = c("--headless", "--disable-gpu", "--no-sandbox", "--disable-images") )) remDr <- remoteDriver(browserName = "chrome", extraCapabilities = eCap)
- 使用显式等待代替固定睡眠:用
remDr$waitForElement替代Sys.sleep,更高效可靠:
# 等待标题元素出现,最多等待5秒 remDr$waitForElement(using = "tag name", value = "title", timeout = 5000)
- 添加错误处理:在foreach循环中加入
tryCatch,避免单个URL爬取失败中断整个任务:
ws <- foreach(x = seq_along(URLsPar), .packages = c("rvest", "magrittr", "RSelenium")) %dopar% { tryCatch({ remDr$navigate(URLsPar[x]) remDr$waitForElement(using = "tag name", value = "title", timeout = 5000) remDr$getTitle()[[1]] }, error = function(e) { message(paste("爬取失败:", URLsPar[x], ",错误信息:", e$message)) return(NA) }) }
内容的提问来源于stack exchange,提问作者Tomas
相关产品推荐
相关产品推荐

