目前在學習《基于R語言的自動數據收集》和《用pyton寫網絡爬蟲》,兩者都介紹網頁數據獲取的相關途徑,前者分析了xml、json、xpath、正則表達式,后者對python內正則表達式、Beautiful Soup和Lxml三者性能比較,如下表:
今天僅僅對正則表達式知識學習整理。Web上的內容主要是無結構的文本。網絡抓取的一項核心任務就是從文本數據堆中采集和我們研究問題相關的信息。
正則表達式
正則表達式是用于搜索和操作文本數據的概括性文本特征。嚴格意義上,與其說它們是一個工具,不如說它們是如何通過各種函數對字符串進行查詢的慣例。本文介紹R中實現擴展正則表達式的基本組成部分。下面字符串會貫穿本文的例子:
R> example.obj <- "1. A small sentence. - 2. Another tiny sentence."
嚴格的字符匹配
最基礎的層次就是字符和字符的匹配,即使正則表達式也是如此。因此,從一個字符串提取一個子串就會得到子串本身,如果有:
R> str_extract(example.obj , "small")
[1] "small"
否則,函數會返回一個缺失值:
R> str_extract(example.obj , "banana")
[1] NA
這里及其它部分使用的函數主要是來自stringr組件的str_extract(),我們假定這個組件的所有后續例子里都已加載了的。該函數的定義是str_extract(string , pattern),這樣首先輸入要被操作的字符串,然后就是要查找的表達式。該函數會在一個給定的字符串里返回與給定正則表達式匹配的第一個實例。我們也可以調用str_extract_all()函數要求R取出每一個匹配的結果。大家有興趣了解R和string字符串操作函數的比較,可以百度資料自行了解。
R> unlist(str_extract_all(example.obj , "sentence"))
[1] "sentence" "sentence"
由于str_extract_all()通常可以對多個字符串進行調用,所以結果是作為一個列表返回的,每個列表元素提供了針對其中的一個字符串的結果。上述調用中,輸入字符串是一個長度為1的字符向量,因此,函數會返回一個長度為1的列表,對它調用unlist()以便解析。下面把上述結果和同時對多個字符串進行調用時國企函數的表現進行比較。我們創建一個包含了"text" "manipulation" "basics"幾個字符串的向量。然后使用str_extract_all()函數來提取符合特征"a"的所有實例:
R> out <-str_extract_all(c("text","manipulation","basics") , "a")
R> out
[[1]]
character(0)
[[2]]
[1] "a" "a"
[[3]]
[1] "a"
該函數返回一個與輸入向量長度(也就是3)相同的列表,列表每個元素包含一個字符串的結果。因為第一個字符串里沒有a,所以第一個元素是一個空字符向量。第二個字符串包含2個a,第三個字符串有1個。
默認情況下,字符匹配是區分字母大小寫的。因此,正則表達式里的大寫字母和小寫字母是不一樣的。
R> str_extract(example.obj , "small")
[1] "small"
在上面的字符串里包含small,而沒有SMALL。
R> str_extract(example.obj , "SMALL")
[1] NA
結果,該函數提取不到匹配的值。我們可以用ignore.case()包裹該字符串,從而改變這種結果。
R> str_extract(example.obj , ignore.case("SMALL"))
[1] "small"
使用正則表達式并不局限于匹配的單詞。一個字符串無非是一個字符的序列。因此,我們也可以匹配字根:
R> unlist(str_extract_all(example.obj , "en"))
[1] "en" "en" "en" "en"
或字母字符和空格的混合。
R> unlist(str_extract_all(example.obj , "mall sent"))
[1] "mall sent"
正則表達式的廣義化
到目前為止,我們只是進行固定表達式的匹配。但正則表達式的威力來源于能夠編寫靈活及廣義化的查詢條件。其中最為廣義化的是句號( . )。它可以匹配任意字符。
R> str_extract(example.obj , "sm.ll")
[1] "small"
在正則表達式中,另一個強大的廣義化是字符類(character class),它被包裹在中括號里[ ]。一個字符類的含義是任何中括號里的字符都會被匹配。
R> str_extract(example.obj , "sm[abc]ll")
[1] "small"
還有另一種方法也可以利用字符范圍來指定字符類的元素,它使用 - 。
R> str_extract(example.obj , "sm[a-p]ll")
[1] "small"
在這種情況下,任何a到p的字符都是合法的匹配。除了字母和數字字符,也可以在正則表達式里包括標點和空格。相類似,它們也可以放在字符類里。例如,字符[uvw.]能匹配u、v、w,也能匹配句號和空格。