楼主: fantuanxiaot
25658 219

[源码分享] [转载]R微博数据爬取/R正则资料   [推广有奖]

Ψ▄┳一大卫卍卐席尔瓦

大师

8%

还不是VIP/贵宾

-

威望
7
论坛币
-234475 个
通用积分
124.1424
学术水平
3783 点
热心指数
3819 点
信用等级
3454 点
经验
150207 点
帖子
7546
精华
32
在线时间
1327 小时
注册时间
2013-2-3
最后登录
2022-2-24

初级学术勋章 初级热心勋章 中级热心勋章 中级学术勋章 初级信用勋章 中级信用勋章 高级热心勋章 高级学术勋章 特级学术勋章 特级热心勋章 高级信用勋章 特级信用勋章

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币

R微博数据爬取



之前一直没有解决web微博的数据抓取问题,所以绕道走wap。但是这两天wap又要开始输验证码了。验证码对我来说实在是太难绕开的关口,于是索性又花了点时间了解前端的那堆脚本,终于能够比较顺利地提取新浪微博的微博数据了,然后又顺手尝试了一下简单的分词和词云,于是我来写个日记。主要是三个函数:登录微博,抓取数据,生成词云。


# 首先还是微博登录的函数。
  1. f_weibo_login <- function(name="********@******", pwd="****"){
  2. memory.limit(4000)
  3. library(RCurl)
  4. library(digest)
复制代码

# 对ID的预处理
name <- gsub('@', '%40', name)
name <- base64(name)[1]

# 常规的打包,具体没仔细研究
  1. myH <- c("Host"="login.sina.com.cn",
  2. "User-Agent"="Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
  3. "Accept"="text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
  4. "Accept-Language"="zh-cn,zh;q=0.5",
  5. "Accept-Encoding"="gzip, deflate",
  6. "Accept-Charset"="GB2312,utf-8;q=0.7,*;q=0.7",
  7. "Keep-Alive"="115",
  8. "Connection"="keep-alive",
  9. "Referer"="http://weibo.com/",
  10. "Content-Type"="application/x-www-form-urlencoded; charset=UTF-8")
  11. d <- debugGatherer()
  12. cH <- getCurlHandle(debugfunction=d$update, verbose=T,
  13. ssl.verifyhost=F, ssl.verifypeer=F, followlocation=T, cookiefile="cc.txt")
复制代码

# 预登录的页面。这里貌似应该用一些正则匹配的,也没有仔细研究
  1. preurl <- paste("http://login.sina.com.cn/sso/prelogin.php?entry=miniblog&callback=sinaSSOController.preloginCallBack&su=",
  2. name, "&client=ssologin.js(v1.3.18)", sep='')
  3. prelogin <- readLines(preurl, warn=F)
  4. servertime <- strsplit(prelogin, '\"servertime\":')[[1]][2]
  5. servertime <- strsplit(servertime, ',\"pcid\"')[[1]][1]
  6. pcid <- strsplit(prelogin, '\"pcid\":\"')[[1]][2]
  7. pcid <- strsplit(pcid, '\",\"nonce\"')[[1]][1]
  8. nonce <- strsplit(prelogin, '\"nonce\":\"')[[1]][2]
  9. nonce <- strsplit(nonce, '\"}')[[1]][1]
  10. servertime
  11. pcid
  12. nonce
  13. # 加密的过程
  14. pwd1 <- digest(pwd, algo='sha1', seria=F)
  15. pwd2 <- digest(pwd1, algo='sha1', seria=F)
  16. pwd3 <- digest(paste(pwd2, servertime, nonce, sep=''), algo='sha1', seria=F)
  17. getCurlInfo(cH)[["cookielist"]]
  18. pinfo=c(
  19. "service"="miniblog",
  20. "client"="ssologin.js(v1.3.18)",
  21. "entry"="weibo",
  22. "encoding"="UTF-8",
  23. "gateway"="1",
  24. "savestate"="7",
  25. "from"="",
  26. "useticket"="1",
  27. "su"=name,
  28. "servertime"=servertime,
  29. "nonce"=nonce,
  30. "pwencode"="wsse",
  31. "sp"=pwd3,
  32. "vsnf"="1",
  33. "vsnval"="",
  34. "pcid"=pcid,
  35. "url"="http://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack",
  36. "returntype"="META",
  37. "ssosimplelogin"="1",
  38. "setdomain"="1"
  39. )
复制代码

# 登录
  1. ttt <- postForm("http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.3.18)",
  2. httpheader=myH, .params=pinfo, curl=cH, style="post")
  3. getCurlInfo(cH)[["cookielist"]]


  4. newurl <- strsplit(ttt[1], 'location.replace\\(\'')[[1]][2]
  5. newurl <- strsplit(newurl, '\'\\);')[[1]][1]
  6. newurl
  7. getURL(newurl, curl=cH, .encoding="gbk")
  8. getCurlInfo(cH)[["cookielist"]]
  9. return(cH)
  10. }
复制代码

# 然后是抓取数据的函数。目前只写了feeds部分的抓取,其他是类似的,而且会更简单一点,不需要刷新页面。
f_weibo_get <- function(cH=ch0, N=200, hisnick='chenyibo'){
# 参数N是想要获取的微博条数。参数hisnick是对方的ID
library(rjson)
memory.limit(4000)


# 先看一下有多少页
  1. pg=1
  2. the1url <- paste('http://weibo.com/', hisnick, '/profile?page=', pg, sep='')
  3. the1get <- getURL(the1url, curl=cH, .encoding="gbk")
  4. write(the1get, "temp.txt")
  5. the1get <- readLines("temp.txt")

  6. idi <- grep('\\[\'oid\'\\]', the1get)
  7. oid <- strsplit(the1get[idi], '\\[\'oid\'\\] = \'')[[1]][2]
  8. oid <- strsplit(oid, '\';')[[1]][1]
  9. idi <- grep('\\[\'uid\'\\]', the1get)
  10. uid <- strsplit(the1get[idi], '\\[\'uid\'\\] = \'')[[1]][2]
  11. uid <- strsplit(uid, '\';')[[1]][1]
复制代码


# 微博信息
  1. infoi <- grep('\"pid\":\"pl_content_litePersonInfo\"', the1get)
  2. a1 <- gsub('<script>STK && STK.pageletM && STK.pageletM.view\\(','',the1get[infoi])
  3. a1 <- gsub('\\)</script>','',a1)
  4. a1 <- fromJSON(a1)$html
  5. write(a1, 'a1.txt')
  6. a1 <- readLines("a1.txt")
  7. numberi <- max(grep('node-type=\"weibo\">', a1))
  8. number <- strsplit(a1[numberi], 'node-type=\"weibo\">')[[1]][2]
  9. number <- strsplit(number, '</strong>')[[1]][1]
  10. pages <- ceiling(min(as.numeric(number), N)/45)
复制代码



weibo_data <- c()


# 循环读取页面
for (pg in 1:pages){

# 第一屏
the1url <- paste('http://weibo.com/', hisnick, '/profile?page=', pg, sep='')
the1get <- getURL(the1url, curl=cH, .encoding="gbk")
write(the1get, "temp.txt")
the1get <- readLines("temp.txt")


# 看别人的时候是hisFeed,看自己的时候是myFeed(后面的url也略有差异,主要是刷新的时候需要用到uid)
if(uid == oid){
myfeedi <- grep('\"pid\":\"pl_content_myFeed\"', the1get)
}
if(uid != oid){
myfeedi <- grep('\"pid\":\"pl_content_hisFeed\"', the1get)
}
a1 <- gsub('<script>STK && STK.pageletM && STK.pageletM.view\\(','',the1get[myfeedi])
a1 <- gsub('\\)</script>','',a1)
a1 <- fromJSON(a1)$html
write(a1, 'a1.txt')
a1 <- readLines("a1.txt")

# 最后一条微博的ID
lastmidi <- max(grep('mid=\"', a1))
lastmid <- strsplit(a1[lastmidi], 'mid=\"')[[1]][2]
lastmid <- strsplit(lastmid, '\"')[[1]][1]

# 于是第二屏
the2url <- paste('http://weibo.com/aj/mblog/mbloglist?page=', pg,
'&count=15&max_id=', lastmid, '&pre_page=', pg, '&end_id=&pagebar=0&uid=', oid, sep='')
the2get <- getURL(the2url, curl=cH, .encoding="gbk")
write(the2get, "temp.txt")
the2get <- readLines("temp.txt")
a2 <- fromJSON(the2get)$data
write(a2, 'a2.txt')
a2 <- readLines("a2.txt")

# 最后一条微博的ID
lastmidi <- max(grep('mid=\"', a2))
lastmid <- strsplit(a2[lastmidi], 'mid=\"')[[1]][2]
lastmid <- strsplit(lastmid, '\"')[[1]][1]

# 于是第三屏
the3url <- paste('http://weibo.com/aj/mblog/mbloglist?page=', pg,
'&count=15&max_id=', lastmid, '&pre_page=', pg, '&end_id=&pagebar=1&uid=', oid, sep='')
the3get <- getURL(the3url, curl=cH, .encoding="gbk")
write(the3get, "temp.txt")
the3get <- readLines("temp.txt")
a3 <- fromJSON(the3get)$data
write(a3, 'a3.txt')
a3 <- readLines("a3.txt")

# 筛选微博正文内容,连接起来
a123 <- c(a1, a2, a3)
index <- grep('node-type=\"feed_list_content\"', a123)
a11 <- a123[index]
b <- gregexpr('>[^<>]*<', a11)
getcontent <- function(string, greg){
paste(substring(string, greg+1, greg+attr(greg,'match.length')-2), collapse=' ')
}
a111 <- mapply(getcontent, a11, b)
names(a111) <- NULL
weibo_data <- c(weibo_data, a111)
gc()
}

# 去掉英文和数字,去掉@对象
weibo_data <- gsub('__@.*__', '', weibo_data)
weibo_data <- gsub('[0-9a-zA-Z]+', '', weibo_data)
return(weibo_data[1:min(as.numeric(number), N)])
}

# 登录
ch0 <- f_weibo_login('我的账号', '我的密码')
ch1 <- f_weibo_login('马甲的账号', '马甲的密码')
# 获取微博数据(这里只做了我自己的版本,10000是个足够大的数字)
weibo_10000_0 <- f_weibo_get(cH=ch0, N=10000, hisnick='chenyibo')
weibo_10000_1 <- f_weibo_get(cH=ch1, N=10000, hisnick='chenyibo')
# 这两个结果有一点点点差异,目前看来,貌似是显示给自己的微博比较全。
all(weibo_10000_0 %in% weibo_10000_1)
# FALSE
all(weibo_10000_1 %in% weibo_10000_0)
# TRUE

# 亮哥@许亮_在路上指导我可以用个人词频与公共词频做比较,来筛选关键词。所以我又做了生成词云的函数。
  1. f_weibo_wordcloud <- function(weibo_data=weibo_10000_0, hisnick='chenyibo'){

  2. # 分词
  3. library(rsmartcn)
  4. f_cut <- function(x){
  5. unlist(strsplit(smartcn(x), ' '))
  6. }
  7. words <- unlist(mapply(f_cut, weibo_data))
  8. words <- words[words != 'na']
  9. words <- words[words != '转发']
复制代码


# 统计词频
words_freq <- sort(table(words), dec=T)
words_names <- names(words_freq)
words_length <- nchar(words_names)

# 加载搜狗实验室的词频文件 http://www.sogou.com/labs/dl/w.html
SogouLabDic <- read.table('SogouLabDic.dic', fill=T, head=F)

words_df <- data.frame(words_names=words_names, words_freq=words_freq, words_length=words_length)
# 只做两个字的词,简单一点。。。

words_df <- words_df[words_df$words_length == 2, ]
names(SogouLabDic)[1] <- 'words_names'
SogouLabDic <- SogouLabDic[SogouLabDic[,1] %in% words_df$words_names, ]

words_df2 <- merge (words_df, SogouLabDic, by='words_names', all.x=T)
# words_df2 <- words_df2[grep('^[NV],',words_df2$V3), ]
# 可以筛选名词和动词。不过似乎没有必要,因为形容词副词什么的也能够体现用词风格嘛

words_df2 <- words_df2[order(-words_df2[,2]), ]
words_df3 <- words_df2[is.na(words_df2$V2), ]
words_df4 <- words_df2[!is.na(words_df2$V2), ]

# 匹配不到的,用原来的词频。匹配到的,就看是否超过平均水平
  1. words_df4$words_freq2 <- words_df4$words_freq/words_df4$V2
  2. words_df4 <- words_df4[words_df4$words_freq2 > mean(words_df4$words_freq2), 1:2]
  3. words_df5 <- rbind(words_df4, words_df3[, 1:2])
复制代码

# 做词云(这个包貌似对中文支持不是很好)
  1. library(wordcloud)
  2. png(paste('weibo_wordcloud_', hisnick, '.png', sep=''),width=500,height=500)
  3. par(mar=c(0,0,0,0))
  4. wordcloud(words_df5$words_names, words_df5$words_freq, min.freq=2,
  5. scale=c(9,1), max.words=50, random.order=F, colors=terrain.colors(50,1))
  6. dev.off()
  7. }
  8. f_weibo_wordcloud(weibo_10000_0, hisnick='chenyibo')
复制代码


二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:weibo login 数据抓取 没有解决 新浪微博 微博 资料

已有 1 人评分学术水平 信用等级 收起 理由
oink-oink + 5 + 5 精彩帖子

总评分: 学术水平 + 5  信用等级 + 5   查看全部评分

本帖被以下文库推荐

沙发
fantuanxiaot 发表于 2015-3-5 09:52:43 |只看作者 |坛友微信交流群

R语言的正则学习1


地址可见:https://bbs.pinggu.org/thread-3285884-1-1.html


与文本编辑器相似,几乎所有的高级编程语言都支持正则表达式。在这样的语境下,“文本”也就是一个字符串,可以执行的操作都是类似的。一些编程语言(比如Perl,JavaScript)会检查正则表达式的语法。
正则表达式是什么?
正则表达式只是一个字符串。没有长度限制,但是,这样的正则表达式长度往往较短。如下所示是一些正则表达式的例子:
I had a S+ day today
[A-Za-z0-9-_]{3,16}
dddd-dd-dd
v(d+)(.d+)*
TotalMessages="(.*?)"
<[^<>]>


这些字符串实际上都是微型计算机程序。正则表达式的语法,实际上是一种轻量级、简洁、适用于特定领域的编程语言。记住这一点,那么你就很容易理解下面的事情:
每一个正则表达式,都可以分解为一个指令序列,比如“先找到这样的字符,再找到那样的字符,再从中找到一个字符。。。”
每一个正则表达式都有输入(文本)和输出(匹配规则的输出,有时是修改后的文本)
正则表达式有可能出现语法错误——不是所有的字符串都是正则表达式
正则表达式语法很有个性,也可以说很恐怖
有时可以通过编译,使得正则表达式执行更快


在实现中,正则表达式还有其他的特点。本文将重点讨论正则表达式的核心语法,在几乎所有的正则表达式中都可以见到这些规则。
特别提示:正则表达式与文件通配语法无关,比如 *.xml
正则表达式的基础语法字符 正则表达式中包含了一系列的字符,这些字符只能匹配它们本身。有一些被称为“元字符”的特殊字符,可以匹配一些特殊规则。
如下所示的例子中,我用红色标出了元字符。
I had a S+ day today
[A-Za-z0-9-_]{3,16}
dddd-dd-dd
v(d+)(.d+)*
TotalMessages="(.*?)"
<[^<>]*>


大部分的字符,包括所有的字母和数字字符,是普通字符。也就意味着,它们只能匹配它们自己,如下所示的正则表达式:
cat
意味着,只能匹配一个字符串,以“c”开头,然后是字符“a”,紧跟着是字符“t”的字符串。
到目前为止,正则表达式的功能类似于
常规的Find功能
Java中的 String.indexOf() 函数
PHP中的 strpos()函数
等等

注意:不做特殊说明,正则表达式中是区分大小写的。但是,几乎所有正则表达式的实现,都会提供一个Flag用来控制是否区分大小写。
点“.”我们第一个要讲解的元字符是“.”。这个符号意味着可以匹配任意一个字符。如下所示的正则表达式:
c.t
意味着匹配“以c开头,之后是任意一个字符,紧跟着是字母t”的字符串。
在一段文本中,这样的正则表达式可以用来找出cat, cot, czt这样的字符串,甚至可以找出c.t这样的组合,但是不能找到ct或者是coot这样的字符串。
使用反斜杠“”可以忽略元字符,使得元字符的功能与普通字符一样。所以,正则表达式
c.t
表示“找到字母c,然后是一个句号(“.”),紧跟着字母t”
反斜杠本身也是一个元字符,这意味着反斜杠本身也可以通过相似的方法变回到普通字符的用途。因此,正则表达式
c\t
表示匹配“以字符c开头,然后是一个反斜杠,紧跟着是字母t”的字符串。
注意!在正则表达式的实现中,.是不能用于匹配换行符的。”换行符“的表示方法在不同实现中也不同。实际编程时,请参考相关文档。在本文中,我认为.是可以匹配任意字符的。实现环境通常会提供一个Flag标志位,来控制这一点。
字符类字符类是一组在方括号内的字符,表示可以匹配其中的任何一个字符。
正则表达式c[aeiou]t,表示可以匹配的字符串是”以c开头,接着是aeiou中的任何一个字符,最后以t结尾”。在文本的实际应用中,这样的正则表达式可以匹配:cat,cet,cit,cot,cut五种字符串。
正则表达式[0123456789]表示匹配任意一个整数。
正则表达式[a]表示匹配单字符a。


包含忽略字符的例子
a


表示匹配字符串[a]
[[]ab]表示匹配的字符为”["或者'']”或者”a”,或者”b”
[\[]]表示匹配的字符为””或者 “[”或者"]“


在字符类中,字符的重复和出现顺序并不重要。[dabaaabcc]与[abc]是相同的
重要提示:字符类中和字符类外的规则有时不同,一些字符在字符类中是元字符,在字符类外是普通字符。一些字符正好相反。还有一些字符在字符类中和字符类外都是元字符,这要视情况而定!
比如,.表示匹配任意一个字符,而[.]表示匹配一个全角句号。这不是一回事!


字符类的范围在字符集中,你可以通过使用短横线来表示匹配字母或数字的范围。
[b-f]与[b,c,d,e,f]相同,都是匹配一个字符”b”或”c”或”d”或”e”或”f”
[A-Z]与[ABCDEFGHIJKLMNOPQRSTUVWXYZ]相同,都是匹配任意一个大写字母。
[1-9]与[123456789]相同,都是匹配任意一个非零数字。


练习使用目前我们已经讲解的正则表达式相关知识,在字典中匹配找到含有最多连续元音的单词,同时找到含有最多连续辅音的单词。
答案[aeiou][aeiou][aeiou][aeiou][aeiou][aeiou] 这样的正则表达式,可以匹配连续含有六个元音的单词,比如 euouae 和 euouaes。
同样的,恐怖的正则表达式[bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz] 可以找到连续含有十个辅音的单词sulphhydryls.
下文中,我们会讲解,怎样有效缩短这样的正则表达式长度。
在字符类之外,短横线没有特殊含义。正则表达式a-z,表示匹配字符串“以a开头,然后是一个短横线,以z结尾”。
范围和单独的字符可能在一个字符类中同时出现:
[0-9.,]表明匹配一个数字,或者一个全角句号,或者一个逗号
[0-9a-fA-F]意味着匹配一个十六进制数
[a-zA-Z0-9-]意味着匹配一个字母、数字或者一个短横线


练习使用已经介绍过的正则表达式知识,匹配YYYY-MM-DD格式的日期。
答案[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9].
同样的,下文中,我们会介绍怎样有效减少这样的正则表达式长度。
虽然你可以尝试在正则表达式中使用一些非字母或数字作为范围的最后一个符号,比如abc[!-/]def,但是这并不是在每种实现中都合法。即使这样的语法是合法的,这样的语义也是模糊的。最好不要这样使用。
同时,你必须谨慎选择范围的边界值。即使[A-z]在你使用的实现中,是合法的,也可能会产生无法预料的运行结果。(注意,在z到a之间,是有字符存在的)
注意:范围的字符值代表的是字符而已,并不能代表数值范围,比如[1-31]表示匹配一个数字,是1或者2或者3,而不是匹配一个数值在1到31之间的数。
字符类的反义你可以在字符类的起始位放一个反义符。
[^a]表示匹配任何不是“a”的字符
[^a-zA-Z0-9]表示匹配任何不是字母也不是数字的字符
[^abc]匹配一个为“^”或者a或者b或者c的字符
[^^]表示匹配任何不为“^”的字符


练习在字典中,找到一个不满足“在e之前有i,但是没有c”的例子。
答案


cie和[^c]ei都要可以找到很多这样的例子,比如ancient,science,viel,weigh
转义字符类d这个正则表达式与[0-9]作用相同,都是匹配任何一个数字。(要匹配d,应该使用正则表达式\d)
w与[0-9A-Za-z]相同,都表示匹配一个数字或字母字符
s意味着匹配一个空字符(空格,制表符,回车或者换行)
另外
D与[^0-9]相同,表示匹配一个非数字字符。
W与[^0-9A-Za-z]相同,表示匹配一个非数字同时不是字母的字符。
S表示匹配一个非空字符。


这些是你必须掌握的字符。你可能已经注意到了,一个全角句号“.”也是一个字符类,可以匹配任意一个字符。
很多正则表达式的实现中,提供了更多的字符类,或者是标志位在ASCII码的基础上,扩展现有的字符类。
特别提示:统一字符集中包含除了0至9之外的更多数字字符,同样的,也包含更多的空字符和字母字符。实际使用正则表达式时,请仔细查看相关文档。
练习简化正则表达式 [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9].
答案dddd-dd-dd.
重复在字符或字符集之后,你可以使用{ }大括号来表示重复
正则表达式a{1}与a意思相同,都表示匹配字母a
a{3}表示匹配字符串“aaa”
a{0}表示匹配空字符串。从这个正则表达式本身来看,它毫无意义。如果你对任何文本执行这样的正则表达式,你可以定位到搜索的起始位置,即使文本为空。
a{2}表示匹配字符串“a{2}”
在字符类中,大括号没有特殊含义。[{}]表示匹配一个左边的大括号,或者一个右边的大括号


练习简化下面的正则表达式
z.......z
dddd-dd-dd
[aeiou][aeiou][aeiou][aeiou][aeiou][aeiou]
[bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz][bcdfghjklmnpqrstvwxyz]


答案
z.{7}z
d{4}-d{2}-d{2}
[aeiou]{6}
[bcdfghjklmnpqrstvwxyz]{10}

注意:重复字符是没有记忆性的,比如[abc]{2}表示先匹配”a或者b或者c”,再匹配”a或者b或者c”,与匹配”aa或者ab或者ac或者ba或者bb或者bc或者ca或者cb或者cc“一样。[abc]{2}并不能表示匹配”aa或者bb或者cc“

指定重复次数范围重复次数是可以指定范围的
x{4,4}与x{4}相同
colou{0,1}r表示匹配colour或者color
a{3,5}表示匹配aaaaa或者aaaa或者aaa


注意这样的正则表达式会优先匹配最长字符串,比如输入 I had an aaaaawful day会匹配单词aaaaawful中的aaaaa,而不会匹配其中的aaa。
重复次数是可以有范围的,但是有时候这样的方法也不能找到最佳答案。如果你的输入文本是I had an aaawful daaaaay那么在第一次匹配时,只能找到aaawful,只有再次执行匹配时才能找到daaaaay中的aaaaa.
重复次数的范围可以是开区间
a{1,}表示匹配一个或一个以上的连续字符a。依然是匹配最长字符串。当找到第一个a之后,正则表达式会尝试匹配尽量多个的连续字母a。
.{0,}表示匹配任意内容。无论你输入的文本是什么,即使是一个空字符串,这个正则表达式都会成功匹配全文并返回结果。


练习使用正则表达式找到双引号。要求输入字符串可能包含任意个字符。
调整你的正则表达式使得在一对双引号中间不再包含其他的双引号。
答案 ".{0,}", 然后 "[^"]{0,}".




关于重复的转义字符?与{0,1}相同,比如,colou?r表示匹配colour或者color
*与{0,}相同。比如,.*表示匹配任意内容
+与{1,}相同。比如,w+表示匹配一个词。其中”一个词”表示由一个或一个以上的字符组成的字符串,比如_var或者AccountName1.
这些是你必须知道的常用转义字符,除此之外还有:
?*+ 表示匹配字符串”?*+”
[?*+]表示匹配一个问号,或者一个*号,或者一个加号


练习简化下列的正则表达式:
".{0,}" and "[^"]{0,}"
x?x?x?
y*y*
z+z+z+z+


答案
".*" and "[^"]*"
x{0,3}
y*
z{4,}


练习写出正则表达式,寻找由非字母字符分隔的两个单词。如果是三个呢?六个呢?
w+W+w+, w+W+w+W+w+, w+W+w+W+w+W+w+W+w+W+w+.
下文中,我们将简化这个正则表达式。


非贪婪匹配正则表达式 “.*” 表示匹配双引号,之后是任意内容,之后再匹配一个双引号。注意,其中匹配任意内容也可以是双引号。通常情况下,这并不是很有用。通过在句尾加上一个问号,可以使得字符串重复不再匹配最长字符。
d{4,5}?表示匹配dddd或者ddddd。也就是和d{4}一样
colou??r与colou{0,1}r相同,表示找到color或者colour。这与colou?r一样。
“.*?”表示先匹配一个双引号,然后匹配最少的字符,然后是一个双引号,与上面两个例子不同,这很有用。
https://bbs.pinggu.org/thread-3285884-1-1.html

使用道具

藤椅
fantuanxiaot 发表于 2015-3-5 09:53:18 |只看作者 |坛友微信交流群

接上文



选择匹配你可以使用|来分隔可以匹配的不同选择:
cat|dog表示匹配”cat”或者”dog”
red|blue|以及red||blue以及|red|blue都表示匹配red或者blue或者一个空字符串
a|b|c与[abc]相同
cat|dog||表示匹配”cat”或者”dog”或者一个分隔符”|“
[cat|dog]表示匹配a或者c或者d或者g或者o或者t或者一个分隔符“|”


练习简化下列正则表达式:
s|t|u|v|w
aa|ab|ba|bb
[abc]|[^abc]
[^ab]|[^bc]
[ab][ab][ab]?[ab]?

答案


[s-w]
[ab]{2}
.
[^b]
[ab]{2,4}

练习使用正则表达式匹配1到31之间的整数,[1-31]不是正确答案!
这样的正则表达式不唯一. [1-9]|[12][0-9]|3[01] 是其中之一。
分组你可以使用括号表示分组:
通过使用 Mon|Tues|Wednes|Thurs|Fri|Satur|Sun)day 匹配一周中的某一天
(w*)ility 与 w*ility 相同。都是匹配一个由”ility”结尾的单词。稍后我们会讲解,为何第一种方法更加有用。
表示匹配一对括号。
[()]表示匹配任意一个左括号或者一个右括号


练习在《时间机器中》找到一对括号中的内容,然后通过修改正则表达式,找到不含括号的内容。
答案.?. 然后是, [()]?.


分组可以包括空字符串:
(red|blue)表示匹配red或者blue或者是一个空字符串
abc()def与abcdef相同


你也可以在分组的基础上使用重复:
(red|blue)?与(red|blue|)相同
w+(s+w+)表示匹配一个或多个由空格分隔的单词


练习简化正则表达式 w+W+w+W+w+ 以及 w+W+w+W+w+W+w+W+w+W+w+.
答案w+(W+w+){2}, w+(W+w+){5}.


单词分隔符在单词和非单词之间有单词分隔符。记住,一个单词w是[0-9A-Za-z_],而非单词字符是W(大写),表示[^0-9A-Za-z_].
在文本的开头和结尾通常也有单词分隔符。
在输入文本it’s a cat中,实际有八个单词分隔符。如果我们在cat之后在上一个空格,那就有九个单词分隔符。.
b表示匹配一个单词分隔符
bwwwb表示匹配一个三字母单词
aba表示匹配两个a中间有一个单词分隔符。这个正则表达式永远不会有匹配的字符,无论输入怎样的文本。


单词分隔符本身并不是字符。它们的宽度为0。下列正则表达式的作用不同
(bcat)b
(bcatb)
b(cat)b
b(catb)


练习在词典中找到最长的单词。
答案在尝试之后发现,b.{45,}b可以在字典中找到最长单词


换行符一篇文本中可以有一行或多行,行与行之间由换行符分隔,比如:
Line一行文字
Line break换行符
Line一行文字
Line break换行符

Line break换行符
Line一行文字


注意,所有的文本都是以一行结束的,而不是以换行符结束。但是,任意一行都可能为空,包括最后一行。
行的起始位置,是在换行符和下一行首字符之间的空间。考虑到单词分隔符,文本的起始位置也可以当做是首行位置。
最后一行是最后一行的尾字符和换行符之间的空间。考虑到单词分隔符,文本的结束也可以认为是行的结束。
那么新的格式表示如下:
Start-of-line, line, end-of-line
Line break
Start-of-line, line, end-of-line
Line break

Line break
Start-of-line, line, end-of-line


基于上述概念:
^表示匹配行的开始位置
$表示匹配行的结束位置
^&表示一个空行
^.*& 表示匹配全文内容,因为行的开始符号也是一个字符,"."会匹配这个符号。找到单独的一行,可以使用 ^.*?$
^表示匹配字符串“^”
[]表示匹配一个。但是,[^]不是合法的正则表达式。记住在方括号中,字符有不同的特殊含义。要想在方括号内匹配^,必须用[^]


与字符分隔符一样,换行符也不是字符。它们宽度为0.如下所示的正则表达式作用不同:
(^cat)$
(^cat$)
^(cat)$
^(cat$)


练习使用正则表达式在《时间机器》中找到最长的一行。
答案使用正则表达式^.{73,}可以匹配长度为73的一行文本分界在很多的正则表达式实现中,将和作为文本的开始符号和结束符号。
还有一些实现中,用A和z作为文本的开始和结束符号。
捕捉和替换从这里开始,正则表达式真正体现出了它的强大。
捕获组你已经知道了使用括号可以匹配一组符号。使用括号也可以捕获子串。假设正则表达式是一个小型计算机程序,那么捕获子串就是它输出的一部分。
正则表达式(w*)ility表示匹配以ility结尾的词。第一个被捕获的部分是由w*控制的。比如,输入的文本内容中有单词accessibility,那么首先被捕获的部分是accessib。如果输入的文本中有单独的ility,则首先被捕获的是一个空字符串。
你可能会有很多的捕获字符串,它们可能靠得很近。捕获组从左向右编号。也就是只需要对左括号计数。
假设有这样的正则表达式:(w+) had a ((w+) w+)
输入的内容是:I had a nice day


捕获组1:I
捕获组2:nice day
捕获组3:nice


在一些正则表达式的实现中,你可以从零开始编号,编号零表示匹配整句话:I had a nice day.
在其他的实现中,如果没有制定捕获组,那么捕获组1会自动地填入捕获组0的信息。
是的,这也意味着会有很多的括号。有一些正则表达式的实现中,提供了“非捕获组”的语法,但是这样的语法并不是标准语法,因此我们不会介绍。
从一个成功的匹配中返回的捕获组个数,与使用原来的正则表达式获得的捕获组个数相同。记住这一点,你可以解释一些奇怪的现象。.
正则表达式((cat)|dog)表示匹配cat或者dog。这里有两个捕获组,如果输入文本是dog,那么捕获组1是dog,捕获组2为空。
正则表达式a(w)*表示匹配一个以a开头的单词。这里只有一个捕获组
如果输入文本为a,捕获组1为空。
如果输入文本为ad,捕获组为d
如果输入文本为avocado,捕获组1为v。但是捕获组0表示整个单词avocado.



使用道具

板凳
fantuanxiaot 发表于 2015-3-5 09:57:31 |只看作者 |坛友微信交流群

转载一个R语言的网页爬虫

  1. #*************网页爬虫-R语言实现,函数库文件*******#
  2. #****作者:oldlee11***************************************#
  3. #****版本:v0.1*******************************************#
  4. #****时间:2012-11-14*************************************#
  5. library(XML);
  6. #****函数:(crawler1)
  7. #****概要:网络抓取的主要函数1,可以抓取n个网页的m个变量。每个xpath只爬取一个数据,如果大于1个则提示有误。(精确抓取)
  8. #****输入:
  9. #        名称           |    数据格式
  10. #        url            |    欲抓取的网站的url                向量:n个
  11. #        xpath          |    给出的抓取变量的xpath            向量:m个
  12. #        content        |    变量是结点的内容还是结点的属性值 向量:m个  
  13. #                            "text"是内容(默认),或者是属性名称
  14. #****输出:只有print,无输出
  15. #        名称           |    含义

  16. crawler1<-function(url,xpath,content=rep("text",length(xpath))){
  17.     #如果xpath以及content的数量不同,则输入数据有误
  18.     num_url<-length(url)
  19.     if(length(content)!=length(xpath)){
  20.         print("Error:content和xpath向量的数量不一致!")
  21.         return
  22.     }

  23.     #建立一个num_url行,num_vari列的数据框
  24.     num_vari<-length(xpath)
  25.     result<-data.frame(rep(0,num_url))
  26.     for(i in 2:num_vari){
  27.         cbind(result,rep(0,num_url))
  28.     }
  29.      
  30.     #遍历url向量,依次对相应网页进行抓取
  31.     i<-1
  32.     j<-1
  33.     for(i_url in url){
  34.         i_url_parse<-htmlParse(i_url,encoding="UTF-8")#读取url网页数据,并使用htmlParse转化。(xml文件使用xmlParse)
  35.         for(j in 1:num_vari){#依次填充一个页面中的不同欲读取的数据值
  36.             node<-getNodeSet(i_url_parse,xpath[j])#通过xpath[i]找到相应变量的xpath结点
  37.             if(length(node)==0){#未爬取到数据,说明xpath有误
  38.                 result[i,j]<-NA
  39.                 print(paste("注意:第",j,"个变量未能在第",i,"个页面中找到,我们会把该数据写为空值"))
  40.             }else if(length(node)==1){#爬取到一个数据,说明正常
  41.                 if(content[j]=="text"){#欲爬取变量的内容
  42.                     result[i,j]<-xmlValue(node[[1]])
  43.                 }else{#欲爬取变量的属性
  44.                     result[i,j]<-xmlGetAttr(node[[1]],content[j])
  45.                     result[i,j]<-iconv(result[i,j],"UTF-8","gbk")#如果是乱码,可以打开此语句。如果是na可以删除此句
  46.                 }
  47.             }else{#爬取到多个数据,本函数不予处理
  48.                 result[i,j]<-NA
  49.                 print(paste("注意:第",j,"个变量能在第",i,"个页面中找到多个,不知您要哪一个,我们会把该数据写为空值"))   
  50.             }
  51.         }
  52.         i<-i+1
  53.     }
  54.     result
  55. }

  56. #****函数:(crawler2)
  57. #****概要:网络抓取的主要函数2,可以抓取n个网页的1个变量。该xpath可以爬取多个数据,(批量抓取)
  58. #****输入:
  59. #        名称           |    数据格式
  60. #        url            |    欲抓取的网站的url                向量:n个
  61. #        xpath          |    给出的抓取变量的xpath            向量:1个
  62. #        content        |    变量是结点的内容还是结点的属性值 向量:1个  
  63. #                            "text"是内容(默认),或者是属性名称
  64. #****输出:只有print,无输出
  65. #        名称           |    含义
  66. #        url            |    1---n自然数,相同url拥有相同数值
  67. #        vari           |    读取的数据
  68. crawler2<-function(url,xpath,content="text"){
  69.     num_url<-length(url)
  70.     result<-data.frame(url=0,vari=0)
  71.     i<-1#记录第几个url
  72.     tmp<-1#
  73.     for(i_url in url){
  74.         i_url_parse<-htmlParse(i_url,encoding="UTF-8")#读取url网页数据,并使用htmlParse转化。(xml文件使用xmlParse)
  75.         node<-getNodeSet(i_url_parse,xpath)#通过xpath[i]找到相应变量的xpath结点
  76.         if(length(node)==0){#未爬取到数据,说明xpath有误
  77.             result[tmp,1]<-i
  78.             result[tmp,2]<-NA
  79.             print(paste("注意:变量未能在第",i,"个页面中找到,我们会把该数据写为空值"))
  80.             tmp<-tmp+1
  81.         }else{
  82.             for(j in 1:length(node)){
  83.                 result[tmp,1]<-i
  84.                 if(content=="text"){#欲爬取变量的内容
  85.                     result[tmp,2]<-xmlValue(node[[j]])
  86.                 }else{#欲爬取变量的属性
  87.                     result[tmp,2]<-xmlGetAttr(node[[j]],content)
  88.                     #result[tmp,2]<-iconv(result[tmp,2],"UTF-8","gbk")#如果是乱码,可以打开此语句。如果是na可以删除此句
  89.                 }
  90.                 tmp<-tmp+1
  91.             }
  92.         }
  93.         i<-i+1
  94.     }
  95.     result
  96. }

  97. #test

  98. #测试crawler1
  99. #测试内容
  100. url1<-"http://3c.taobao.com/detail.htm?spm=872.217037.254698.6.deIiSJ&spuid=205341228&cat=1101"
  101. url2<-"http://3c.taobao.com/detail.htm?spm=872.217037.254698.11.deIiSJ&spuid=203228104&cat=1101"
  102. url3<-"http://item.taobao.com/item.htm?spm=1020.3.9.122.SCNhDn&id=15695321398&from="
  103. url<-c(url1,url2,url3)
  104. xpath<-c("//div[@id='idetail']//div[@class='info-area']//div[@class='tlt clearfix']//h1","//div[@id='idetail']//div[@class='info-area']//div[@class='key-info']//span[@class='price']")
  105. crawler1(url,xpath)
  106. #测试属性值
  107. url<-"http://data.caixin.com/macro/macro_indicator_more.html?id=F0001&cpage=2&pageSize=30&url=macro_indicator_more.html#top";
  108. xpath<-"//meta[@name='keywords']"
  109. content<-"content"
  110. crawler1(url,xpath,content)

  111. #测试crawler2
  112. url<-"http://list.taobao.com/itemlist/bao.htm?spm=567.116925.155171.105.9ZYYMX&cat=50072693&isprepay=1&viewIndex=1&yp4p_page=0&commend=all&atype=b&style=grid&olu=yes&isnew=2&smc=1&mSelect=false&user_type=0&fl=50072693#!cat=50072693&isprepay=1&user_type=0&as=0&viewIndex=1&yp4p_page=0&commend=all&atype=b&style=grid&olu=yes&isnew=2&mSelect=false&smc=1&json=on&tid=0"
  113. xpath<-"//li[@class='list-item list-item-grid']"
  114. content<-"data-commenturl"
  115. crawler2(url1,xpath,content)
  116. #疑难:如何破解data-commenturl="{{item.commendHref}}"
  117. #<li class="list-item list-item-grid" data-isrush="{{item.isLimitPromotion}}" data-ismall="{{item.isMall}}" data-item="{{item.itemId}}" data-#params="sellerId={{item.sellerId}}&ip={{extraInfo.userIp}}" data-comment="{{item.commend}}" data-commenturl="{{item.commendHref}}" data-#virtual="{{item.isVirtual}}">

  118. url2<-"D://r//lab//网页爬虫//data//bao.htm"
  119. crawler2(url2,xpath,content)
复制代码

使用道具

报纸
fantuanxiaot 发表于 2015-3-5 09:58:12 |只看作者 |坛友微信交流群

接上文




替换假如你使用了一个正则表达式去匹配字符串,你可以描述另外一个字符串来替换其中的匹配字符。用来替换的字符串称为替换表达式。它的功能类似于
常规的Replace会话
Java中的String.replace()函数
PHP的str_replace()函数
等等


练习将《时间机器》中所有的元音字母替换为r。
答案使用正则表达式[aeiou]以及[AEIOU],对应的替换字符串分别为r,R.


但是,你可以在替换表达式中引用捕获组。这是在替换表达式中,你可以唯一操作的地方。这也是非常有效的,因为这样你就不用重构你找到的字符串。
假设你正在尝试将美国风格的日期表示MM/DD/YY替换为ISO 8601日期表示YYYY-MM-DD
从正则表达式(dd)/(dd)/(dd)开始。注意,这其中有三个捕获组:月份,日期和两位的年份。


.捕获组的内容和捕获组编号之间用反斜杠分隔,因此你的替换表达式应该是203-1-2.
如果我们输入的文本中包含03/04/05表示2005年3月4日那么:
捕获组1:03
捕获组2:04
捕获组3:05
替换字符串2005-03-04.


在替换表达式中,你可以多次使用捕获组
对于双元音,正则表达式为([aeiou]),替换表达式为ll
在替换表达式中不能使用反斜杠。比如,你在计算机程序中希望使用字符串中使用部分文本。那么,你必须在每个双引号或者反斜杠之前加上反斜杠。


你的正则表达式可以是([\"])。捕获组1是双引号或者反斜杠
你的替换表达式应该是\l


在某些实现中,采用美元符号$代替


练习使用正则表达式和替换表达式,将23h59这样的时间戳转化为23:59.
答案正则表达式finds the timestamps, 替换表达式1:2


反向引用在一个正则表达式中,你也可以引用捕获组。这称作:反向引用
比如,[abc]{2}表示匹配aa或者ab或者ac或者ba或者bb或者bc或者ca或者cb或者cc.但是{[abc]}1表示只匹配aa或者bb或者cc.
练习在字典中,找到包含两次重复子串的最长单词,比如papa, coco
b(.{6,})1b 匹配 chiquichiqui.
如果我们不在乎单词的完整性,我们可以忽略单词的分解,使用正则表达式 (.{7,})1匹配countercountermeasure 以及 countercountermeasures.
使用正则表达式编程特别提醒:
过度使用的反斜杠在一些编程语言,比如Java中,对于包含正则表达式的字符串没有特殊标记。字符串有着自己的过滤规则,这是优先于正则表达式规则的,这是频繁使用反斜杠的原因。
比如在Java中
匹配一个数字,使用的正则表达式从d变为代码中的String re= “\d”
匹配双引号字符串的正则表达式从"[^"]*" 变为String re = “”[^"]*”"
匹配反斜杠或者是左边方括号,或者右边方括号的正则表达式从[\[]] 变为String re = “[\\]”;
String re = "\s"; 和String re = "[ trn]"; 是等价的. 注意它们实际执行调用时的层次不同。


在其他的编程语言中,正则表达式是由特殊标明的,比如使用/。下面是JavaScript的例子:
匹配一个数字,d会简单写成 var regExp = /d/;.
匹配一个反斜杠或者一个左边的方括号或者一个右边的方括号, var regExp = /[\[]]/;
var regExp = /s/; 和 var regExp = /[ trn]/; 是等价的
当然,这意味着在使用/时必须重复两次。比如找到URL必须使用var regExp = /https?:///;.


我希望现在你能明白,我为什么让你特别注意反斜杠。
动态正则表达式当你动态创建一个正则表达式的时候请特别小心。如果你使用的字符串不够完善的花,可能会有意想不到的匹配结果。这可能导致语法错误,更糟糕的是,你的正则表达式语法正确,但是结果无法预料。
错误的Java代码:
String sep = System.getProperty(“file.separator”); String[] directories = filePath.split(sep);
Bug:String.split() 认为sep是一个正则表达式。但是,在Windows中,Sep是表示匹配一个反斜杠,也就是与正则表达式”\”相同。这个正则表达式是正确的,但是会返回一个异常:PatternSyntaxException.
任何好的编程语言都会提供一种良好的机制来跳过字符串中所有的元字符。在Java中,你可以这样实现:
String sep = System.getProperty(“file.separator”);
String[] directories = filePath.split(Pattern.quote(sep));
循环中的正则表达式将正则表达式字符串加入反复运行的程序中,是一种开销很大的操作。如果你可以在循环中避免使用正则表达式,你可以大大提高效率。
其他建议输入验证正则表达式可以用来进行输入验证。但是严格的输入验证会使得用户体验较差。比如:
信用卡号在一个网站上,我输入了我的卡号比如 1234 5678 8765 4321 网站拒绝接收。因为它使用了正则表达式d{16}。
正则表达式应该考虑到用户输入的空格和短横线。
实际上,为什么不先过滤掉所有的非数字字符,然后再进行有效性验证呢?这样做,可以先使用D以及空的替换表达式。
练习在不先过滤掉所有的非数字字符的情况下,使用正则表达式验证卡号的正确性。
答案D*(dD*){16} is one of several variations which would accomplish this.


名字不要使用正则表达式来验证姓名。实际上,即使可以,也不要企图验证姓名。
程序员对名字的错误看法:
名字中不含空格
名字中没有连接符号
名字中只会使用ASCII码字符
名字中出现的字都在特殊字符集中
名字至少要有M个字的长度
名字不会超过N个字的长度
人们只有一个名
人们只有一个中间名
人们只有一个姓(最后三条是从英语的人名考虑)


电子邮件地址不要使用正则表达式验证邮箱地址的正确性。
首先,这样的验证很难是精确的。电子邮件地址是可以用正则表达式验证的,但是表达式会非常的长并且复杂。
短的正则表达式会导致错误。(你知道吗?电子邮箱地址中会有一些注释)
第二,即使一个电子邮件地址可以成功匹配正则表达式,也不代表这个邮箱实际存在。邮箱的唯一验证方法,是发送验证邮件。
注意在严格的应用场景中,不要使用正则表达式来解析HTML或者XML。解析HTML或者XML:
使用简单的正则表达式不能完成
总体来说非常困难
已经有其他的方法解决


找到一个已经有的解析库来完成这个工作
这就是55分钟的全部内容总结:
字符: a b c d 1 2 3 4 etc.
字符类: . [abc] [a-z] d w s
. 代表任何字符
d 表示“数字”
w   表示”字母”, [0-9A-Za-z_]
s   表示 “空格, 制表符,回车或换行符”
否定字符类: [^abc] D W S


重复: {4} {3,16} {1,} ? * +
? 表示 “零次或一次”
* 表示 “大于零次”
+ 表示 “一次或一次以上”
如果不加上?,所有的重复都是最长匹配的(贪婪)
分组: (Septem|Octo|Novem|Decem)ber
词,行以及文本的分隔: b ^ $ A z
转义字符: 1 2 3 etc. (在匹配表达式和替换表达式中都可用)
元字符: .  [ ] { } ? * + | ( ) ^ $
在字符类中使用元字符: [ ]  - ^
使用反斜杠可以忽略元字符:

使用道具

地板
河岸栏杆 发表于 2015-3-5 09:59:38 |只看作者 |坛友微信交流群

回帖奖励 +6

{:2_37:}

使用道具

7
河岸栏杆 发表于 2015-3-5 10:00:27 |只看作者 |坛友微信交流群

回帖奖励 +6

使用道具

回帖奖励 +6

嘿嘿。不错额!!!!

使用道具

9
河岸栏杆 发表于 2015-3-5 10:01:50 |只看作者 |坛友微信交流群

回帖奖励 +6

使用道具

10
fjrong 在职认证  发表于 2015-3-5 10:02:24 |只看作者 |坛友微信交流群

回帖奖励 +6

谢谢分享!

使用道具

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注jr
拉您进交流群

京ICP备16021002-2号 京B2-20170662号 京公网安备 11010802022788号 论坛法律顾问:王进律师 知识产权保护声明   免责及隐私声明

GMT+8, 2024-4-24 20:29