数据挖掘:R语言02 网络爬虫之rvest包

老惯例,进入正文前先po出文章架构(如下):

一、基本信息

在实际工作中我们有时需要获取互联网上的非结构化数据,那么就涉及到网络爬虫知识。能写网络爬虫的语言很多,比如Perl,PHP,Python,R语言等,各有利弊,但不管好的坏的,能抓到有用的数据都是好的。本文继续使用R语言来写网络爬虫,但本文只涉及基础爬虫,涉及IP代理、模拟访问等知识以后再写。

二、涉及知识

辅助工具

浏览器:谷歌Chrome或火狐Firefox(此文以Chrome为例)

网页结构知识

认得出<div><a><href>等标签,然后有样学样就行。要说清这部分知识,估计得多码几万字,估计大伙也不愿意看,我也“难产”ヘ(_ _ヘ)

如何定位

本文以Q房网深圳高档小区,房价,二手房源,个人,买房,二手房出售-深圳Q房网

第一步:打开浏览器(好废话的感觉),输入上述地址;

第二步:在弹出页面上任一一家小区名称,点击右键“检查”,然后显示下图内容(左边为正常内容,右边是网页源代码,比如我点了第一家小区,那么浏览器直接定位到源代码中存放该小区名称位置);

第三步:仔细观察层级关系(图中打圈圈就是我们要留意的);

第四步:爬取的数据页面不只一页,点击下一页,观察链接如何变化。

源链接:深圳高档小区,房价,二手房源,个人,买房,二手房出售-深圳Q房网

点击下一页:变为深圳高档小区,房价,二手房源,个人,买房,二手房出售-深圳Q房网/n2

点击第6页,为深圳高档小区,房价,二手房源,个人,买房,二手房出售-深圳Q房网/n6 , 应该看出规律了吧?

三、所需函数

%>% 管道函数,表示将左侧值代入右侧函数中的第一个参数里。

read_html( ) 读取网页

read_html(x, encoding = "", ..., options = c("RECOVER", "NOERROR", "NOBLANKS")) #x 链接 #encoding = #解码 UTF-8

html_nodes( ) 从网页文件中选取代码

html_nodes(x, css, xpath) #css与 xpath 路径二选一即可

假设我们要提取丽沙花都的小区名,其源代码是

css路径表示为:div.show-detail p.house-title a

xpath路径表示为:xpath= "//p[@class=house-title]//a

(双斜杠表示绝对路径; 获取xpath路径tips:详见底部更新)

html_text( ) 从代码中抽离文本

html_text(x, trim = FALSE)#trim选择是否去掉空格

html_attr( )从代码中抽离属性值

html_attr(x)

四、案例

在开始真正的爬虫之前,我们再梳理一下爬虫工作步骤:

1,识别网站链接以及变化;

2,构造链接

3,编写单个网页的爬虫代码;

4,循环

1,识别网站链接以及变化;(见二、涉及知识)

2,构造链接

url <- str_c(basicURL,"/n",m)

3,编写单个网页的爬虫代码;

library(rvest) library(stringr) basicURL <- "" WebSpider <- function(m){ url <- str_c(basicURL,"/n",m) web <- read_html(url,encoding = "UTF-8") name <- web %>% html_nodes("p.house-title")%>%html_text() #获取小区名 con_time <- web %>% html_nodes("p.house-about.clearfix span")%>% html_text() #获取建造时间,网页代码中层级关系aboutyuclearfix有空格,那么就用英文小数点号代替,不能留空 con_time <- con_time %>% .[seq(2,length(con_time),4)] %>% str_extract("[0-9]+") %>% as.numeric() #seq()序列函数,有疑问?seq()即可查看 #str_extract()为stringr包函数;[0-9]+ 为正则表达式,表示多次匹配并抽取数字 address <- web %>% html_nodes("p.house-address.clearfix a.address") %>% html_text()#小区地址 rent <- web %>% html_nodes("div.show-price span.sale-price") %>% html_text() #租金 rent <- rent %>% .[seq(1,length(rent),2)] %>% as.numeric() #若不加seq(),rent的结果包含租金和房价,原理同con_time unitPrice <- web %>% html_nodes("div.show-price span.sale-price") %>% html_text() unitPrice <- unitPrice %>% .[seq(2,60,2)] %>% as.numeric() #合并成数据框 data.frame(name,con_time,rent,unitPrice,address) }

4,循环

results <- data.frame() for(m in 1:78){ #78为小区信息的总页码 results <- rbind(results,WebSpider(m))#合并每一次循环输出的数据 }

爬取的数据如下图(我只爬取750个小区左右)

> head(results) name con_time rent unitPriceaddress 1 国展苑 2003 2913 34014 深圳市龙岗区布吉镇吉荣路 2 中海康城国际 2010 3179 40386 龙岗中心城黄阁路转清辉路(向... 3 金地梅陇镇 2007 5153 55122龙华镇梅龙大道与布龙公路交汇... 4布吉阳光花园,祺峰华庭 2003 3267 38799深圳市龙岗布吉布李路与新龙路... 5 中海大山地花园 2007 3716 36793 横岗梧桐路与环城北路交汇处 6 金地名津,金地口岸天街 2007 5693 55620 落马洲大桥与深圳河交汇处

五、数据展示

1,小区地理位置分布(使用REmap包,可参考

数据挖掘:R语言01 地图可视化REmap包)

数据清洗library(REmap) geoData <- get_geo_position(as.character(results$address)) #部分地址无法获取经纬度,只获取了618条 Warning message: In get_geo_position(as.character(results$address)) :龙华镇梅龙大道与布龙公路交汇... not found. 横岗梧桐路与环城北路交汇处 not found. 地铁3号线横岗地铁站B出口 not found. 草埔村清水河特区管理线旁,罗... not found. 龙华人民南路与布龙公路交界处 not found. 中山大道与体育二路交汇处 not found. 新湖路与裕安一路交汇处 not found. 横岗红棉一路与康乐路交汇处 not found. 南山北环路与龙珠大道的交界处 not found. 湖东路 not found. 梅龙路梅陇镇南侧 not found. 布吉镇政府东北侧 not found. 深圳市龙岗布吉镇布龙公路与深... not found. 龙华永香路与民康路交界处 not found. 龙华街道梅龙南路东侧 not found. 工业大道与工业四路交界西南 not found. 龙华新城金龙路与新区大道交汇... not found. 金牛西路12号 not found. 坪山新区政府及中心公园旁not found. 华侨城片区世界之窗对面 not found. 龙珠大道西丽中学附近 not found. 华侨城香山中路 not found. 南山深南大道旁科技园生态园区... not found. 布吉街道锦龙路与惠康路交汇处 not found. 新洲三路和福强路交汇处 not found. 创业二路与宝民一路交汇处 not found. 中山大道与体育二路交汇处 not found. 深圳市龙岗布吉老街中心、北布... not found. ?[... truncated]

因部分小区无法获取经纬度,可通过merge()函数来匹配已查找到经纬度的地址

metadata <- merge(geoData,results,by.x = "city",by.y = "address") #合并后出现715条数据

合并后出现重复数据,原因可能是用于获取经纬度的地址存在相同,但可以通过unique()函数去除重复数据

metadata <- unique(metadata) #最终获得616条数据 数据整理#对metadata中的字段重新排列 metadata <- metadata[,c(4:7,1:3)] #结果如下图小区分布情况distr_data <- metadata[,c(6,7,1)] remapB(markPointData = data.frame(distr_data$name), markPointTheme = markPointControl(symbol = "pin", symbolSize = 8, color = "red"), geoData = distr_data)

(时间较为匆忙,没有做过多分析)

六、其他

1,实际操作中会遇到如网站无法连接接、数据框合并出错、被目标官网的发爬虫机制识别等问题,但本文目前只是提到基本爬虫,因此没有写出一一对应的解决方案,后续的文章会陆续给出解决方案。

2,本文还涉及到stringr包和正则表达式,涉及的内容也不少,因而也计划放在后面来写。

3,此文本应中午就发了,中途因为自己按错键删掉码好的几千字,最可悲的是知乎还自动保存了!!!没办法,只能重新码字,写得比较匆忙,所以可能会有些错误。欢迎指出,我会及时更正。

4,第二篇文章,仍希望大家多拍砖、多交流。

(好了,要滚了,老板催着我出项目结果,估计今晚又要加班,互联网996伤不起呀)

***更新三、所需函数内容补充

xpath路径获取tips:

1,将鼠标放在想提取的内容上(不是源代码);

2,然后右键,点击“检查”;

3,浏览器右侧会自动定位到内容的源代码上;

4,在源代码上点击右键,然后弹出一个列表,选择第四个“copy”;

5,在弹出的选项中,选择“Copy Xpath”;

6,完成!

本文中丽沙花都小区名的xpath路径,直接复制得到的内容为://*[@id="list-content"]/div[2]/div[1]/h3/a,但放到html_nodes()时要注意,需要将list-content的双引号改为单引号。