杜雨,EasyCharts团队成员,R语言中文社区专栏作者,兴趣方向为:Excel商务图表,R语言数据可视化,地理信息数据可视化。个人公众号:数据小魔方(微信ID:datamofang) ,“数据小魔方”创始人。

在抓取数据时,很大一部分需求是抓取网页上的关系型表格。

对于表格而言,R语言和Python中都封装了表格抓取的快捷函数,R语言中XML包中的readHTMLTables函数封装了提取HTML内嵌表格的功能,rvest包的read_table()函数也可以提供快捷表格提取需求。Python中read_html同样提供直接从HTML中抽取关系表格的功能。

HTML语法中内嵌表格有两类,一类是table,这种是通常意义上所说的表格,另一类是list,这种可以理解为列表,但从浏览器渲染后的网页来看,很难区分这两种,因为效果上几乎没有差异,但是通过开发者工具的后台代码界面,table和list是两种截然不同的HTML元素。

以上所说到的函数是针对HTML文档中不同标签设计的,所以说如果不加区分的使用这些函数提取表格,很可能对于那些你认为是表格,但是是实际上是list的内容无效。

library("RCurl")library("XML")library("magrittr")library("rvest")

针对XML包而言,一共有三个HTML元素提取的快捷函数,分别是针对HTML表格元素,列表元素,和链接元素,这些快捷函数都是:

readHTMLTable()   #获取网页表格readHTMLList()     #获取网页列表getHTMLlinks()     #从HTML网页获取链接

readHTMLTable

readHTMLTable(doc,header=TRUE)#the HTML document which can be a file name or a URL or an #already parsed HTMLInternalDocument, or an HTML node of class #XMLInternalElementNode, or a character vector containing the HTML #content to parse and process.

该函数支持的HTML文档格式非常广泛,doc可以是一个url链接,可以是一个本地html文档,可以是一个已经解析过的HTMLInternalDocument部件,或者提取出来的HTML节点,甚至包含HTML语法元素的字符串向量。

以下是一个案例,也是我自学爬虫时爬过的网页,后来可能有改版,很多小伙伴儿用那些代码爬不出来,问我咋回事儿。自己试了以下也不行,今天借机重新梳理思路。

大连市2016年空气质量数据可视化~

URL<-"https://www.aqistudy.cn/historydata/monthdata.php?city=北京" %>% xml2::url_escape(reserved ="][!$&'()*+,;=:/?@#")####关于网址转码,如果你不想使用函数进行编码转换,可以通过在线转码平台转码后赋值黏贴使用,但是这不是一个好习惯,在封装程序代码时无法自动化。#http://tool.oschina.net/encode?type=4#R语言自带的转码函数URLencode()转码与浏览器转码结果不一致,所以我找了很多资料,在xml2包里找打了rvest包的url转码函数,稍微做了修改,现在这个函数你可以放心使用了!(注意里面的保留字)###
mydata<-readHTMLTable(URL,header=TRUE)
#Warning message:
#XML content does not seem to be XML: #'https://www.aqistudy.cn/historydata/monthdata.php?city=%E5%8C%97%E4%BA%AC'
header<-c("User-Agent"="Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36")
mytable<-getURL(URL,httpheader=header,.encoding="UTF-8") %>% htmlParse(encoding ="UTF-8") %>% readHTMLTable(header=TRUE)

结果竟然是空的,我猜测这个网页一定是近期做过改版,里面加入了一些数据隐藏措施,这样除了浏览器初始化解析可以看到数据表之外,浏览器后台的network请求链接里都看不到具体数据。

这样既没有API链接,又无法请求道完整网页怎么办呢?别怕,我们不是还有Selenium大法,不行我们就暴力抓取呀!

本次使用Rselenium包,结合plantomjs浏览器来抓取网页。(关于配置可以直接百度,此类帖子很多,主要是版本对应,相应路径加入环境变量)。

###启动selenium服务:cd D:\
java -jar selenium-server-standalone-3.3.1.jar###以上代码在PowerShell中运行,启动selenium服务器。#创建一个remoteDriver对象,并打开library("RSelenium")
remDr <- remoteDriver(browserName = "phantomjs")
remDr$open() #访问登录的页面remDr$navigate("https://www.aqistudy.cn/historydata/monthdata.php?city=%E5%8C%97%E4%BA%AC")
mytable<-remDr$getPageSource()[[1]] %>% htmlParse(encoding ="UTF-8") %>% readHTMLTable(header=TRUE,which =1)
mytable<-remDr$getPageSource()[[1]] %>% read_html(encoding ="UTF-8") %>% html_table(header=TRUE) %>% `[[`(1)#关闭remoteDriver对象remDr$close()

以上两者是等价的,我们获取了一模一样的表格数据,数据预览如下:

DT::datatable(mytable)

readHTMLTable函数和rvest函数中的html_table都可以读取HTML文档中的内嵌表格,他们是很好的高级封装解析器,但是并不代表它们可以无所不能。

毕竟巧妇难为无米之炊,首先需要拿米才能下锅,所以我们在读取表格的时候,最好的方式是先利用请求库请求(RCurl或者httr),请求回来的HTML文档再使用readHTMLTable函数或者html_table函数进行表格提取,否则将无功而反,遇到今天这种情况的,明明浏览器渲染后可以看到完整表格,然后后台抓取没有内容,不提供API访问,也拿不到完整的html文档,就应该想到是有什么数据隐藏的设置。

没关系见招拆招嘛,既然浏览器能够解析,那我就驱动浏览器获取解析后的HTML文档,返回解析后的HTML文档,之后的工作就是使用这些高级函数提取内嵌表格了。

那么selenium服务器+plantomjs无头浏览器帮我们做了什么事呢,其实只做了一件事——帮我们做了一个真实的浏览器请求,这个请求是由plantomjs无头浏览器完成的,它帮我们把经过渲染后的完整HTML文档传送过来,这样我们就可以使用readHTMLTable函数或者read_table()

在XML包中,还有另外两个非常好用的高阶封装函数:

一个用于抓取链接,一个用于抓取列表。

readHTMLList
getHTMLLinks

http://www.tianqi.com/air/

我随便找了一个天气网首页,有全国各大城市的空气指数数据。这个看似是一个表格,实际不一定,我们可以使用现有表格函数试一试。

url<-"http://www.tianqi.com/air/"mylist <-getURL(url,httpheader=header,.encoding="UTF-8") %>% htmlParse(encoding ="gbk") %>% readHTMLTable(header=TRUE)
mylist < url %>%  read_html(encoding ="gbk") %>% html_table(header=TRUE) %>% `[[`(1)NULL

使用以上代码抓内容是空的,原因有两种情况,一种是html里面标签根本不是table格式,有可能是list,另外一种情况可能跟上例一样,表格数据被隐藏。看一下源码就知道这个版块其实是list无序列表存储的,所以使用readtable肯定行不通,这时候就是readHTMLList函数大显身手的时候了。

header<-c(      "User-Agent"="Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36")
mylist <-getURL(url,httpheader=header,.encoding="windows-1253") %>% htmlParse() %>% readHTMLList()  %>% `[[`(4)  %>% .[2:length(.)]
mylist <-read_html(url,encoding="UTF-8") %>% html_nodes(".thead li") %>% html_text()  %>% `[[`(4)  %>% .[2:length(.)]
mylist <-read_html(url,encoding="UTF-8") %>% htmlParse() %>% readHTMLList()  %>% `[[`(4)

虽然成功的获取到了结果,但是遇到了令人厌恶的编码问题,不想跟各种编码斗智斗勇,再次使用了phantomjs无头浏览器,毕竟作为浏览器总是可以正确的解析并渲染网页内容,无论HTML文档的编码声明有多么糟糕!

#cd D:\
#java -jar selenium-server-standalone-3.3.1.jar
#创建一个remoteDriver对象,并打开library("RSelenium")
remDr <- remoteDriver(browserName = "phantomjs")
remDr$open() #访问登录的页面remDr$navigate("http://www.tianqi.com/air/")
mylist<-remDr$getPageSource()[[1]] %>% htmlParse(encoding="utf-8") %>% readHTMLList() %>% `[[`(8) %>% .[2:length(.)]#关闭remoteDriver对象remDr$close()

这次终于看到了希望,果然plantomjs浏览器的渲染效果非同一般!

使用str_extract()函数提取城市id、城市名称、城市污染物指数、污染状况。

library("stringr")
pattern<-"(\\d{1,})([\\u4e00-\\u9fa5]{1,})"mylist<-data.frame(
ID   = mylist %>% str_extract_all(pattern) %>% do.call(rbind,.) %>% .[,1] %>% str_extract("\\d{1,}"),
City = mylist %>% str_extract_all(pattern) %>% do.call(rbind,.) %>% .[,1] %>% str_extract("[\\u4e00-\\u9fa5]{1,}"),
AQI  = mylist %>% str_extract_all(pattern) %>% do.call(rbind,.) %>% .[,2] %>% str_extract("\\d{1,}"),
Quity= mylist %>% str_extract_all(pattern) %>% do.call(rbind,.) %>% .[,2] %>% str_extract("[\\u4e00-\\u9fa5]{1,}")
)
DT::datatable(mylist)

最后一个函数便是抓取网址链接的高级封装函数,因为在html中,网址的tag一般都比较固定,跳转的网址链接一般在<a>标签的href属性中,图片链接一般在<img>标签下的src属性内,比较好定位。

随便找一个知乎的摄影帖子,高清图多的那种!

url<-"https://www.zhihu.com/question/35017762"mylink <-getURL(url,httpheader=header,.encoding="utf-8") %>% htmlParse() %>% getHTMLLinks()[1] "/"                                   "/"                                   "/explore"                           [4] "/topic"                              "/topic/19551388"                     "/topic/19555444"                    [7] "/topic/19559348"                     "/topic/19569883"                     "/topic/19626553"                    [10] "/people/geng-da-shan-ren"            "/people/geng-da-shan-ren"            "/question/35017762/answer/240404907"[13] "/people/he-xiao-pang-zi-30"          "/people/he-xiao-pang-zi-30"          "/question/35017762/answer/209942092"

getHTMLLinks(doc, externalOnly = TRUE, xpQuery = “//a/@href”,baseURL = docName(doc), relative = FALSE)

通过getHTMLLinks的源码可以看到,该函数过滤的链接的条件仅仅是标签下的href属性内的链接,我们可以通过修改xpQuery内的apath表达式参数来获取图片链接。

mylink <-getURL(url,httpheader=header,.encoding="utf-8") %>% htmlParse() %>% getHTMLLinks(xpQuery = "//img/@data-original")

这样轻而易举的就拿到了该知乎摄影帖子的所有高清图片原地址,效率也高了很多。



Python:

python中如果不用爬虫工具,目前我所知道的表格提取工具就是pandas中的read_html函数了,他相当于一个I/O函数(同其他的read_csv,read_table,read_xlsx等函数一样)。同样适用以上R语言中第一个案例的天气数据,直接利用pd.read_html函数也无法获取表格数据,原因相同,html文档中有数据隐藏设定。

import pandas as pd
url="https://www.aqistudy.cn/historydata/monthdata.php?city=%E5%8C%97%E4%BA%AC"dfs = pd.read_html(url)

这里我们同样使用Python中的selenium+plantomjs工具来请求网页,获取完整的源文档之后,使用pd.read_html函数进行提取。

from selenium import webdriver
driver = webdriver.PhantomJS()
driver.get('https://www.aqistudy.cn/historydata/monthdata.php?city=%E5%8C%97%E4%BA%AC')
dfs = pd.read_html(driver.page_source,header=0)[0]
driver.quit()

OK,简直不能再完美,对于网页表格数据而言,pd.read_html函数是一个及其高效封装,但是前提是你要确定这个网页中的数据确实是table格式,并且网页没有做任何的隐藏措施。

在线课程请点击文末原文链接:
往期案例数据请移步本人GitHub:
https://github.com/ljtyduyu/DataWarehouse/tree/master/File

相关课程推荐

R语言爬虫实战案例分享:

网易云课堂知乎live、今日头条、B站视频


分享内容:本次课程所有内容及案例均来自于本人平时学习练习过程中的心得和笔记总结,希望借此机会,将自己的爬虫学习历程与大家分享,并为R语言的爬虫生态改善以及工具的推广,贡献一份微薄之力,也是自己爬虫学习的阶段性总结。

☟☟☟ 戳阅读原文,即刻加入课程。

左手用R右手Python系列之——表格数据抓取之道相关推荐

  1. python中factor函数_左手用R右手Python系列——因子变量与分类重编码

    原标题:左手用R右手Python系列--因子变量与分类重编码 欢迎关注天善智能 hellobi.com,我们是专注于商业智能BI,大数据,数据分析领域的垂直社区,学习.问答.求职,一站式搞定! 对商业 ...

  2. python的rbind_左手用R右手Python系列—数据合并与追加

    原标题:左手用R右手Python系列-数据合并与追加 感谢关注天善智能,走好数据之路↑↑↑ 欢迎关注天善智能,我们是专注于商业智能BI,大数据,数据分析领域的垂直社区,学习,问答.求职一站式搞定! 今 ...

  3. python正则表达式中的r与转义字符_左手用R右手Python系列13——字符串处理与正则表达式...

    原标题:左手用R右手Python系列13--字符串处理与正则表达式 杜雨,EasyCharts团队成员,R语言中文社区专栏作者,兴趣方向为:Excel商务图表,R语言数据可视化,地理信息数据可视化.个 ...

  4. python用merge匹配和左连接_左手用R右手Python系列——数据合并与追加

    感谢关注天善智能,走好数据之路↑↑↑ 欢迎关注天善智能,我们是专注于商业智能BI,大数据,数据分析领域的垂直社区,学习,问答.求职一站式搞定! 本文作者:天善智能社区专家杜雨 今天这篇跟大家介绍R语言 ...

  5. python索引右往左_左手用R右手Python系列5——数据切片与索引

    数据框索引: 基于数据框本身提取 subset函数 filter+select函数 Python: 数据框自身的方法 ix方法 loc方法 iloc方法 query方法 数据框自身的方法 ix方法 l ...

  6. python 变量 r_左手用R右手Python系列——因子变量与分类重编码

    今天这篇介绍数据类型中因子变量的运用在R语言和Python中的实现. 因子变量是数据结构中用于描述分类事物的一类重要变量.其在现实生活中对应着大量具有实际意义的分类事物. 比如年龄段.性别.职位.爱好 ...

  7. [Python爬虫] 三、数据抓取之Requests HTTP 库

    往期内容提要: [Python爬虫] 一.爬虫原理之HTTP和HTTPS的请求与响应 [Python爬虫] 二.爬虫原理之定义.分类.流程与编码格式 一.urllib 模块 所谓网页抓取,就是把URL ...

  8. Python实现的淘宝直通车数据抓取(1)

    最近帮一个朋友做一个抓取淘宝直通车数据的小项目,感觉ython比较适合写爬虫程序,决定使用Python来做程序. 首先是登陆程序,因为淘宝的登陆校验很复杂,所以不能直接使用命令行的形式输入账号密码.查 ...

  9. [Python爬虫] 四、数据抓取之HTTP/HTTPS抓包工具Fiddler

    往期内容提要: [Python爬虫] 一.爬虫原理之HTTP和HTTPS的请求与响应 [Python爬虫] 二.爬虫原理之定义.分类.流程与编码格式 [Python爬虫] 三.数据抓取之Request ...

  10. Python实现的淘宝直通车数据抓取(3)

    商家那里给出的是一个有几千行的excel表,这里使用openpyxl来读取excel. class read_write: def __init__(self,token,cookiestr): se ...

最新文章

  1. 在IIS上安装 thinkphp的方法
  2. python 调用sqldr_sqlldr并发
  3. 4g网络什么时候淘汰_5G时代,4G将淘汰?4G手机会不会像2g,突然失去网络
  4. 安卓7.0拍照遇到 Uri暴露错误
  5. visual studio enter键代码自动补全
  6. 美国空手道俱乐部的 Python 库原来长这样!
  7. OpenGL基础5:第一个正方形
  8. mysql 两个日期加减_mysql加减时间-函数-时间加减
  9. 软件源码破解工具De4Dot用法,net破解、反混淆
  10. H7-TOOL发布固件V2.12,正式发布脱机烧录1拖16支持,脱机烧录增加NXP的MKL系列支持,更新串口助手,CAN助手等(2022-01-09)
  11. 详解24个经典股票技术指标(一)
  12. 2023年南开大学税务专硕考研上岸前辈备考经验指导
  13. Mac系统重置PostgreSQL密码
  14. 三星苹果还能笑多久?联想挑战智能手机全球霸主
  15. 二级分类 php 两张表,PHP_php smarty 二级分类代码和模版循环例子,二级分类的数据表结构如下: - phpStudy...
  16. 现代函数概念的“序偶”定义
  17. Linux树莓派开发——刷机
  18. @Autowired vs @PersistenceContext for EntityManager bean
  19. 教你小小JAVA爬虫爬到HDU首页(只为学习)
  20. 面试|C# .net 面试题

热门文章

  1. 冒充中科大少年班校友被打脸!AI 公司创始人被扒清华硕士和斯坦福博士均造假...
  2. 你还在用HTTP吗?HTTP/3都来了。。。
  3. 快讯:Nginx 之父被抓了!
  4. 女程序员上班第一件事:调整IDE颜色以适配今天的衣着妆容
  5. Linux shell 中的那些你值得一试的小把戏
  6. 我和一位快递小哥的故事
  7. 参加一场 Google IO 需要多少资金?
  8. 对象可以创建数组吗_女生没房可以吗?男生有多在意结婚对象的家境?答案很现实...
  9. Hbase笔记:批量导入
  10. VS2010制作dll