RSelenium包抓取链家网(上:模拟点击与页面抓取)

2023-10-15 12:40

本文主要是介绍RSelenium包抓取链家网(上:模拟点击与页面抓取),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

安装RSelenium包

# 直接从CRAN下载RSelenium包
install.packages("RSelenium")

启动Selenium服务器

  • 在控制台输入java -jar D:\R\library\Rwebdriver\selenium-server-standalone-3.7.1.jar以启动Selenium服务器。保持打开状态,可配合plantomjs、Chrome或Firefox等浏览器使用
  • 本次案例选择chrome浏览器自动抓取,需要下载相应的浏览器驱动(参考Selenium环境配置第3部分)

思路

Created with Raphaël 2.1.2 链家网惠阳二手房总页面 Step1:遍历总页面所有页数,获得房屋链接linkinfo Step2:遍历所有房屋链接,获得房屋信息houseinfo End
  • 页面准备
library(rvest)
library(stringr)
library(RSelenium)
remDr <- remoteDriver(browserName = "chrome")
# 创建一个remoteDriver对象,这一步指定用chrome打开网页,但网页尚未打开
base1 <- "https://hui.lianjia.com/ershoufang/"
base2 <- c("danshui", "huiyangqu", "nanzhanxincheng")
url   <- paste(base1, base2, "/", sep = "")
# 只有淡水、惠阳区、南站新城3个地区,故选择手动拼接url
  • Step1:封装函数LinkinfoFunc
LinkinfoFunc <- function(remDr, url) {result <- data.frame()# 建立空数据框result,后用于盛装数据remDr$open()# 打开chrome浏览器for (i in seq_along(url)) {remDr$navigate(url[i])#驱动浏览器访问第i个地区的首页j = 0# j是第i个地区的页面计数器,即第i个地区第j页,j的初始值为0while (TRUE) {# while循环将遍历第i个地区的所有页数j = j + 1# 开始页面计数,j=1destination <- remDr$getPageSource()[[1]] %>% read_html()# 获取当前访问页面的内容link        <- destination %>% html_nodes("li.clear div.title a") %>% html_attr("href")# 获取当前访问页面中的房屋链接(30条/页)pageinfo    <- destination %>% html_nodes("div.house-lst-page-box") %>% html_attr("page-data") %>% str_extract_all(., ":[\\d]+") %>% unlist() %>% gsub(":", "", .)totalpage   <- pageinfo[1]curpage     <- pageinfo[2]# 从源代码中,提取当前页&总页数的信息data        <- data.frame(link, stringsAsFactors = FALSE)# 将获取到的房屋链接整合为数据框dataresult      <- rbind(result, data)# 将data写入空数据框resultif (curpage != totalpage) {# 若当前页还不是总页数,即尚未达到最后一页cat(sprintf("第【%d】个地区第【%d】页抓取成功", i, j), sep = "\n")# 则输出“第i个地区第j页(即浏览器当前停留页面)抓取成功”的提示信息remDr$executeScript("arguments[0].click();", list(remDr$findElement("css", "div.house-lst-page-box a.on+a")))# 并模拟点击下一页} else {cat(sprintf("第【%d】个地区第【%d】页抓取成功", i, j), sep = "\n")break}}cat(sprintf("第【%d】个地区抓取成功", i), sep = "\n")# 输出“第i个地区抓取成功”的提示信息,返回for循环,继续抓取第i+1个地区}remDr$close()# 所有地区所有页面抓取完毕,跳出for循环,关闭浏览器窗口cat("All work is done!", sep = "\n")return(result)# 返回循环叠加后的最终数据
}
  • Step2:封装函数HouseinfoFunc
HouseinfoFunc <- function(link) {result <- data.frame()for (i in seq_along(link)) {destianation <- read_html(link[i], encoding = "UTF-8")# 获取第i条房屋链接的页面内容location     <- destianation %>% html_nodes("a.no_resblock_a") %>% html_text()# 小区位置unit         <- destianation %>% html_nodes(".price span.unit") %>% html_text()totalprice   <- destianation %>% html_nodes(".price span.total:nth-child(1)") %>%html_text() %>% paste(., unit, sep = "")# 总售价(万)downpayment  <- destianation %>% html_nodes(".taxtext span") %>% html_text() %>% .[1]# 首付(万)persquare    <- destianation %>% html_nodes("span.unitPriceValue") %>% html_text()# 每平米售价(元/平米)area         <- destianation %>% html_nodes(".area .mainInfo") %>% html_text()# 面积大小(平米)title        <- destianation %>% html_nodes(".title h1") %>% html_text()# 标题subtitle     <- destianation %>% html_nodes(".title div.sub") %>% html_text()# 副标题room         <- destianation %>% html_nodes(".room .mainInfo") %>% html_text()# 户型floor        <- destianation %>% html_nodes(".room .subInfo") %>% html_text()# 楼层data         <- data.frame(location, totalprice, downpayment, persquare, area, title, subtitle, room, floor)result       <- rbind(result, data)cat(sprintf("第【%d】条房屋链接抓取成功", i), sep = "\n")}cat("All work is done!", sep = "\n")return(result)
}
  • 执行函数
linkinfo  <- LinkinfoFunc(remDr, url) %>% unlist()
# 执行函数LinkinfoFunc,得到linkinfo(list形式)
houseinfo <- HouseinfoFunc(linkinfo)
# 执行函数HouseinfoFunc,得到houseinfo(data.frame形式)
View(houseinfo)
write.table(houseinfo, row.names = FALSE, sep = ",", "houseinfo.csv")
# View()函数查看数据并导出到本地
  • 查看数据


总结

  • 本例使用chrome浏览器打开网页,因此不需要伪造User-Agent。如使用phantomjs无头浏览器,则要伪造一个User-Agent:
myheader <- list(phantomjs.page.settings.userAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20120101 Firefox/29.0")
remDr    <- remoteDriver(browserName = "phantomjs", extraCapabilities = myheader)
  • 链家网的数据其实并不需要动用Selenium服务器,所有房屋信息在源代码中都能找到。另外,url的变化也十分有规律,最原始的方法是查看各个地区的页数,然后手动构造所有页数,例如,淡水共46页、惠阳区共4页、南站新城共27页:
base <- "https://hui.lianjia.com/ershoufang/"
url1 <- paste0(base, "danshui/pg", 1:46, "/") 
url2 <- paste0(base, "huiyangqu/pg", 1:4, "/") 
url3 <- paste0(base, "nanzhanxincheng/pg", 1:27, "/")
url  <- c(url1, url2, url3)
  • 这里主要用RSelenium包尝试点击下一页的功能,因为各地区总页数不一定总是46、4、27,如果手动构造,容易漏掉或重复抓取。模拟点击可以通过if函数判断当前页是否达到最后一页,yes则停止,no则继续点击。过程中遇到一些错误,并得到迂回解决:

    【错误一】自动点击下一页出现问题,有两种解决办法:

    1. 一是直接点击“下一页”这个按钮(如果页面有这个按钮的话),直接找到这个按钮的css或xpath
    2. 二是点击“当前页+1”(如当前第1页,则直接点击第2页),相邻兄弟选择器(+):本例a.on是当前页,+a即下一页

    【错误二】点击过程中,页面有无关的导航栏挡住“下一页”按钮,导致无法点击并报错,尝试两种办法:

    1. 一是换为phantomjs来驱动模拟点击,而非chrome
    2. 二是使用函数executeScript执行一个JavaScript片段arguments[0].click();。该函数会将JavaScript片段视为参数来执行,而JavaScript片段可以返回首个(JavaScript中索引值是以0位基准)webElement,并实现点击。
  • 由于基本没有设置拦截,数据抓取速度约1-2条/秒


参考资料
RSelenium and Javascript
click on next button :rseleniun
左手用R右手Python系列——动态网页抓取与selenium驱动浏览器

这篇关于RSelenium包抓取链家网(上:模拟点击与页面抓取)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/217766

相关文章

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

usaco 1.2 Transformations(模拟)

我的做法就是一个一个情况枚举出来 注意计算公式: ( 变换后的矩阵记为C) 顺时针旋转90°:C[i] [j]=A[n-j-1] [i] (旋转180°和270° 可以多转几个九十度来推) 对称:C[i] [n-j-1]=A[i] [j] 代码有点长 。。。 /*ID: who jayLANG: C++TASK: transform*/#include<

【测试】输入正确用户名和密码,点击登录没有响应的可能性原因

目录 一、前端问题 1. 界面交互问题 2. 输入数据校验问题 二、网络问题 1. 网络连接中断 2. 代理设置问题 三、后端问题 1. 服务器故障 2. 数据库问题 3. 权限问题: 四、其他问题 1. 缓存问题 2. 第三方服务问题 3. 配置问题 一、前端问题 1. 界面交互问题 登录按钮的点击事件未正确绑定,导致点击后无法触发登录操作。 页面可能存在

hdu4431麻将模拟

给13张牌。问增加哪些牌可以胡牌。 胡牌有以下几种情况: 1、一个对子 + 4组 3个相同的牌或者顺子。 2、7个不同的对子。 3、13幺 贪心的思想: 对于某张牌>=3个,先减去3个相同,再组合顺子。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOExcepti

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

C# 防止按钮botton重复“点击”的方法

在使用C#的按钮控件的时候,经常我们想如果出现了多次点击的时候只让其在执行的时候只响应一次。这个时候很多人可能会想到使用Enable=false, 但是实际情况是还是会被多次触发,因为C#采用的是消息队列机制,这个时候我们只需要在Enable = true 之前加一句 Application.DoEvents();就能达到防止重复点击的问题。 private void btnGenerateSh

【算法专场】模拟(下)

目录 前言 38. 外观数列 算法分析 算法思路 算法代码 1419. 数青蛙 算法分析 算法思路 算法代码  2671. 频率跟踪器 算法分析 算法思路 算法代码 前言 在前面我们已经讲解了什么是模拟算法,这篇主要是讲解在leetcode上遇到的一些模拟题目~ 38. 外观数列 算法分析 这道题其实就是要将连续且相同的字符替换成字符重复的次数+

模拟实现vector中的常见接口

insert void insert(iterator pos, const T& x){if (_finish == _endofstorage){int n = pos - _start;size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;reserve(newcapacity);pos = _start + n;//防止迭代