爬虫进阶(一)

这里我们介绍网页链接的提取以及简单的文本分析。

网页链接提取

前文已经提到现在很多网页都是js渲染过的,我们得用rdom才可以快速爬取。
我们要爬的网站是http://jnqx.jinan.gov.cn/col/col14936/index.html,大体是这个样子的:

示例

我们要的是人影简报(用于反馈人工增雨情况)的链接。

导入库

一般总会用到这么几个库

> library(rdom)
> library(XML)
> library(tidyverse)
> library(rvest)

探索

先尝试一个网页

url <- 'http://jnqx.jinan.gov.cn/col/col14936/index.html?uid=25751&pageNum=1'
x <- rdom(url) %>% getNodeSet(path = "//a[@target = '_blank' and @ class = 'bt_linkb']")

详细介绍一下第二行:
大家知道rdom库就只有一个函数,我们把网页读进来之后再把它传给readHTMLTable(header = T)去分析(参考上一篇)。但是我们发现这个策略对于提取网页表格文字、数字内容非常好用,但是它不能返回超链接。这就麻烦了。
那怎么办呢?去guthub寻找源代码。https://github.com/cpsievert/rdom/blob/master/R/rdom.R

#' Return DOM of a website as HTML
#'
#' @param url A url or a local path.
#' @param css a string containing one or more CSS selectors separated by commas.
#' @param all logical. This controls whether \code{querySelector} or
#' \code{querySelectorAll} is used to extract elements from the page.
#' When FALSE, output is similar to \code{rvest::html_node}. When TRUE,
#' output is similar to \code{rvest::html_nodes}.
#' If \code{css} is missing, then this argument is ignored.
#' @param timeout maximum time to wait for page to load and render, in seconds.
#' @param filename A character string specifying a filename to store result
#' @export
#' @importFrom XML htmlParse
#' @importFrom XML xmlChildren
#' @importFrom XML getNodeSet
#'
#' @examples \dontrun{
#' library("rvest")
#' stars <- "http://www.techstars.com/companies/stats/"
#' # doesn't work
#' html(stars) %>% html_node(".table75") %>% html_table()
#' # should work
#' rdom(stars) %>% html_node(".table75") %>% html_table()
#' # more efficient
#' stars %>% rdom(".table75") %>% html_table()
#' }
#'

rdom <- function(url, css, all, timeout, filename) {
  if (missing(url)) stop('Please specify a url.')
  args <- list(
    system.file('rdomjs/rdom.js', package = 'rdom'),
    url,
    # NA is a nice default since jsonlite::toJSON(NA) == null
    css %||% NA,
    all %||% FALSE,
    timeout %||% 5,
    filename %pe% NA
  )
  args <- lapply(args, jsonlite::toJSON, auto_unbox = TRUE)
  phantom_bin <- find_phantom()
  res <- if (missing(filename)) {
    # capture output as a character vector
    system2(phantom_bin, args = as.character(args),
            stdout = TRUE, stderr = TRUE, wait = TRUE)
  } else {
    # ignore stdout/stderr and write to file
    system2(phantom_bin, args = as.character(args),
            stdout = FALSE, stderr = FALSE, wait = TRUE)
  }
  st <- attr(res, 'status')
  if (!is.null(st)) stop(paste(res, '\n'))
  p <- if (missing(filename)) {
    XML::htmlParse(res, asText = TRUE)
  } else {
    XML::htmlParse(filename)
  }
  # If the result is a node or node list, htmlParse() inserts them into
  # the body of a bare-bones HTML page.
  if (!missing(css)) {
    nodes <- XML::xmlChildren(XML::getNodeSet(p, '//body')[[1]])
    if (length(nodes) == 1) nodes[[1]] else nodes
  } else {
    p
  }
}

通过源代码,我们知道它还可以传递给XML库的getNodeSet函数。事实上,rdom返回的是"XMLInternalElementNode" "XMLInternalNode" "XMLAbstractNode",只要能接受这三种数据类型之一的函数就可以用。当然很多是不接受的。
我们研究getNodeSet怎么用,

nodeset <- getNodeSet(doc = , path = ) 

其中doc就是rdom的返回值,path比较麻烦,它是用xpath语法写的。可以参见XPath 语法,很简单。类似于正则表达式。
具体来看这个例子,

示例
我们需要href = 里面的东西,构造

path = "//a[@target = '_blank' and @ class = 'bt_linkb']"

就是说要标签<a>里面的而且参数target = '_blank' 、class = 'bt_linkb的。
你当然可以用正则表达式弄,不过麻烦点罢了。
看一下我们的成果:

> x[[1]]
<a href="/art/2010/9/25/art_14936_554027.html" class="bt_linkb" target="_blank" title="2010人影简
报第五期(总第95期)">2010人影简报第五期(总第95期)</a>

基本快搞定了,最后来一波正则表达式把链接弄出来。然而出现了一个问题,当我们尝试把x[[1]]转化为字符串时,报错了:

> as.character(x[[1]])
Error in as.vector(x, "character") : 
  cannot coerce type 'externalptr' to vector of type 'character'

几个意思呢?说x[[1]]是一个外部指针,无法强制转化为字符串。
经过google上的一番折腾,应该这样处理指针:

capture.output(x[[1]])

这样就捕获了这个指针指向的内容。应该说,R语言中很少见这种事。
正则表达式就比较好办了,有一个套路:

m <- regexpr('/art/[0-9]*/[0-9]*/[0-9]*/art_[0-9]*_[0-9]*.html', c[j])
c[j] <- substr(c[j], m, attr(m, "match.length") - 1 + m)

全部程序如下:

s <- 0
L <- list()
url_2 <- 'http://jnqx.jinan.gov.cn'

for (k in 1:8) {
  
  url <- 'http://jnqx.jinan.gov.cn/col/col14936/index.html?uid=25751&pageNum='
  url <- paste(url, k, sep = '')
  x <- rdom(url) %>% getNodeSet(path = "//a[@target = '_blank' and @ class = 'bt_linkb']")
  c <- character(length(x))
  
  for (j in 1:length(x)) {
    c[j] <- capture.output(x[[j]])
    m <- regexpr('/art/[0-9]*/[0-9]*/[0-9]*/art_[0-9]*_[0-9]*.html', c[j])
    c[j] <- substr(c[j], m, attr(m, "match.length") - 1 + m)
  }

  c <- paste(url_2, c, sep = '')
  
  for (i in 1:length(c)) {
    webpage <- read_html(c[i], encoding="utf-8")
    data_html <- html_nodes(webpage, '#zoom')
    L[[s + i]] <- html_text(data_html)
  }
  
  
  s <- s + length(c)
}

事实上写爬虫也是个苦差事…

文本分析

源码在这里:

t <- character(length(L))
for (i in 1:length(L)) {
  m <- regexpr('[0-9]*月[0-9]*日', L[[i]])
  t[i] <- substr(L[[i]], m, attr(m, "match.length") - 1 + m)
}

t[20] <- '6月22日'
t[88] <- '5月17日'
t[89] <- '5月7日'
t[100] <- '11月3日'
t[108] <- '3月21日'
t[111] <- '11月17日'
t[114] <- '10月26日'
L[[130]] <- NULL
t <- t[-130]

r <- character(length(L))
for (i in 1:length(L)) {
  m <- regexpr(''[0-9]+?\\.[0-9]毫米'', L[[i]])
  r[i] <- substr(L[[i]], m, attr(m, "match.length") - 1 + m)
}

data <- data.frame(Time = t, result = r)

其实主要就是正则表达式,+?表示非贪婪匹配,也就是匹配到一开始那个就成功了。注意R语言字符串转义要用\\而不是\。
到此,下面这些文本就分析好了。剩下的该回归回归,该机器学习就学习。

示例


总结

  1. 正则表达式要掌握好
  2. 有问题github上看源码
  3. StackExhcange要用好
  4. 掌握具体问题具体分析原则
  5. 不要轻易放弃熟悉的工具
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容

  • 不同城市,不同地区,都会有不同的地域风貌,所以本地类公众号多少都会带有一些地域特色。 大家微信里多多少少也会关注几...
    小库存阅读 3,433评论 0 2
  • 首先要明确心理咨询与心理治疗的准确定义。 心理咨询(counseling)的定义是: 心理咨询是通过来访者与咨询师...
    风墟阅读 1,091评论 0 6
  • 曾梦想仗剑走天涯 文/青衣 没看过原著,是冲着林更新去看了《三少爷的剑》,没想到和我想象中的不太一样。 看前半部分...
    青衣小2阅读 412评论 0 0
  • 国外翻译四大名著,已经算重新起名了,他们把《红楼梦》翻译成《The Story of Stone》,就是石头记,比...
    朗月明轩阅读 1,710评论 0 1