【问题标题】:Scraping a wiki page for the "Periodic table" and all the links为“周期表”和所有链接抓取一个 wiki 页面
【发布时间】:2010-12-09 00:33:48
【问题描述】:

我希望抓取以下 wiki 文章:http://en.wikipedia.org/wiki/Periodic_table

这样我的 R 代码的输出将是一个包含以下列的表格:

  • 化学元素简称
  • 化学元素全称
  • 化学元素 wiki 页面的 URL

(显然,每个化学元素都有一行)

我正在尝试使用 XML 包获取页面内的值,但似乎一开始就卡住了,所以我希望有一个关于如何做到这一点的示例(和/或相关示例的链接)

library(XML)
base_url<-"http://en.wikipedia.org/wiki/Periodic_table"
base_html<-getURLContent(base_url)[[1]]
parsed_html <- htmlTreeParse(base_html, useInternalNodes = TRUE)
xmlChildren(parsed_html)
getNodeSet(parsed_html, "//html", c(x = base_url))
[[1]]
attr(,"class")
[1] "XMLNodeSet"

【问题讨论】:

  • 您为什么要废弃该页面?从其他地方获取您的数据,定期系统不会经常更改。这些链接必须遵循某种模式......
  • 如果不是你,Tal,问这个问题我会很怀疑,但我知道你的动机一定很纯粹。该页面是“受保护的”,因此即使 R 有方便的工具来访问特殊的 Wiki 界面,它们也可能无法用于此操作。尝试转到此页面:en.wikipedia.org/w/…
  • 语言点:'scrape'是'scraping'的动词。 “报废”的意思是把它变成垃圾! 'Scrap' 得到两个 'p' - 所以'scrapping a page' 意味着把它变成垃圾,'scraping a page' 是从中获取数据!
  • @Tal:我没有意识到这会是一个多么好的问题。两个很棒的回复!
  • 每个维基媒体页面都可以返回 XML 格式而不是 HTML - 例如en.wikipedia.org/wiki/Special:Export/Periodic_Table 在这里阅读更多:mediawiki.org/wiki/Manual:Parameters_to_Special:Export

标签: xml r web-scraping


【解决方案1】:

试试这个:

library(XML)

URL <- "http://en.wikipedia.org/wiki/Periodic_table"
root <- htmlTreeParse(URL, useInternalNodes = TRUE)

# extract attributes and value of all 'a' tags within 3rd table
f <- function(x) c(xmlAttrs(x), xmlValue(x))
m1 <- xpathApply(root, "//table[3]//a", f)
m2 <- suppressWarnings(do.call(rbind, m1))

# extract rows that correspond to chemical symbols
ix <- grep("^[[:upper:]][[:lower:]]{0,2}", m2[, "class"])

m3 <- m2[ix, 1:3]
colnames(m3) <- c("URL", "Name", "Symbol")
m3[,1] <- sub("^", "http://en.wikipedia.org", m3[,1])
m3[,2] <- sub(" .*", "", m3[,2])

一点输出:

> dim(m3)
[1] 118   3
> head(m3)
     URL                                      Name        Symbol
[1,] "http://en.wikipedia.org/wiki/Hydrogen"  "Hydrogen"  "H"   
[2,] "http://en.wikipedia.org/wiki/Helium"    "Helium"    "He"  
[3,] "http://en.wikipedia.org/wiki/Lithium"   "Lithium"   "Li"  
[4,] "http://en.wikipedia.org/wiki/Beryllium" "Beryllium" "Be"  
[5,] "http://en.wikipedia.org/wiki/Boron"     "Boron"     "B"   
[6,] "http://en.wikipedia.org/wiki/Carbon"    "Carbon"    "C"   

我们可以通过从 Jeffrey 的 xpath 表达式开始进一步增强 xpath 表达式(因为它几乎将元素置于顶部)并为其添加一个限定条件,从而使其更紧凑。在这种情况下,xpathSApply 可用于消除对do.call 或 plyr 包的需要。我们修复零碎的最后一点和以前一样。这会产生一个矩阵而不是数据框,这似乎更可取,因为内容完全是字符。

library(XML)

URL <- "http://en.wikipedia.org/wiki/Periodic_table"
root <- htmlTreeParse(URL, useInternalNodes = TRUE)

# extract attributes and value of all a tags within 3rd table
f <- function(x) c(xmlAttrs(x), xmlValue(x))
M <- t(xpathSApply(root, "//table[3]/tr/td/a[.!='']", f))[1:118,]

# nicer column names, fix up URLs, fix up Mercury.
colnames(M) <- c("URL", "Name", "Symbol")
M[,1] <- sub("^", "http://en.wikipedia.org", M[,1])
M[,2] <- sub(" .*", "", M[,2])

View(M)

【讨论】:

  • 你好 Grothendieck - 很棒的答案 - 两个竖起大拇指(和一个“选择”的答案)。感谢您的帮助!最好的,塔尔
  • 我在争论选择谁作为“答案”。由于杰弗里(另一个答案)只有 18 个业力点(他也提供了一个可行的答案),我决定给他“V 标记”。但是你的回答很有帮助。再次感谢你!最好的,塔尔
  • 基于 Jeffrey 的 xpath 表达式和我之前的代码的增强添加了第二个解决方案。
  • @Gabor:我已经对你的第一个回复投了赞成票,所以我所能做的就是对这个进一步的评论投赞成票,并为此加上书面的额外感谢以及对 rhelp 的所有其他优秀回复。
  • 我赞同 DWin 的观点 - 确实是一个很好的答案!
【解决方案2】:

Tal——我认为这很容易。我将向您指出 readHTMLTable(),我最喜欢 XML 包中的函数。哎呀,它的帮助页面甚至显示了一个抓取维基百科页面的示例!

但是很可惜,这不是你想要的:

library(XML)
url = 'http://en.wikipedia.org/wiki/Periodic_table'
tables = readHTMLTable(html)

# ... look through the list to find the one you want...

table = tables[3]
table
$`NULL`
         Group #    1    2    3     4     5     6     7     8     9    10    11    12     13     14     15     16     17     18
1         Period      <NA> <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>   <NA>   <NA>   <NA>   <NA>   <NA>   <NA>
2              1   1H       2He  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>   <NA>   <NA>   <NA>   <NA>   <NA>   <NA>
3              2  3Li  4Be         5B    6C    7N    8O    9F  10Ne  <NA>  <NA>  <NA>   <NA>   <NA>   <NA>   <NA>   <NA>   <NA>
4              3 11Na 12Mg       13Al  14Si   15P   16S  17Cl  18Ar  <NA>  <NA>  <NA>   <NA>   <NA>   <NA>   <NA>   <NA>   <NA>
5              4  19K 20Ca 21Sc  22Ti   23V  24Cr  25Mn  26Fe  27Co  28Ni  29Cu  30Zn   31Ga   32Ge   33As   34Se   35Br   36Kr
6              5 37Rb 38Sr  39Y  40Zr  41Nb  42Mo  43Tc  44Ru  45Rh  46Pd  47Ag  48Cd   49In   50Sn   51Sb   52Te    53I   54Xe
7              6 55Cs 56Ba    *  72Hf  73Ta   74W  75Re  76Os  77Ir  78Pt  79Au  80Hg   81Tl   82Pb   83Bi   84Po   85At   86Rn
8              7 87Fr 88Ra   ** 104Rf 105Db 106Sg 107Bh 108Hs 109Mt 110Ds 111Rg 112Cn 113Uut 114Uuq 115Uup 116Uuh 117Uus 118Uuo
9                <NA> <NA> <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>   <NA>   <NA>   <NA>   <NA>   <NA>   <NA>
10 * Lanthanoids 57La 58Ce 59Pr  60Nd  61Pm  62Sm  63Eu  64Gd  65Tb  66Dy  67Ho  68Er   69Tm   70Yb   71Lu          <NA>   <NA>
11  ** Actinoids 89Ac 90Th 91Pa   92U  93Np  94Pu  95Am  96Cm  97Bk  98Cf  99Es 100Fm  101Md  102No  103Lr          <NA>   <NA>

名字不见了,原子序数进入符号。

所以回到绘图板......

我的 DOM walk-fu 不是很强大,所以这不是很漂亮。它获取表格单元格中的每个链接,仅保留具有“标题”属性的链接(即符号所在的位置),并将您想要的内容粘贴到 data.frame 中。它也可以获取页面上的所有其他此类链接,但我们很幸运,元素是前 118 个此类链接:

library(XML)
library(plyr) 

url = 'http://en.wikipedia.org/wiki/Periodic_table'

# don't forget to parse the HTML, doh!

doc = htmlParse(url)

# get every link in a table cell:

links = getNodeSet(doc, '//table/tr/td/a')

# make a data.frame for each node with non-blank text, link, and 'title' attribute:

df = ldply(links, function(x) {
            text = xmlValue(x)
            if (text=='') text=NULL

            symbol = xmlGetAttr(x, 'title')
            link = xmlGetAttr(x, 'href')
            if (!is.null(text) & !is.null(symbol) & !is.null(link))
                data.frame(symbol, text, link)
        } )

# only keep the actual elements -- we're lucky they're first!

df = head(df, 118)

head(df)
     symbol text            link
1  Hydrogen    H  /wiki/Hydrogen
2    Helium   He    /wiki/Helium
3   Lithium   Li   /wiki/Lithium
4 Beryllium   Be /wiki/Beryllium
5     Boron    B     /wiki/Boron
6    Carbon    C    /wiki/Carbon

【讨论】:

  • 您好杰弗里,感谢您的回答!我喜欢看你是如何实现它的。我还建议您添加缺少的“doc
  • 经过深思熟虑(并且由于我最终使用了您的代码片段),我决定将“V 标记”移至您的答案。我希望在这里见到你更多 - 你的回答很棒(ps:当我开始时,我也想过使用 readHTMLTable 函数,然后很快发现它的局限性)。干杯,塔尔
  • 非常感谢 - 很抱歉错过了解析调用!我也喜欢 G. Grothendieck 的回答,但我很欣赏这些观点!
【解决方案3】:

你必须刮维基百科吗?您可以改为针对 Wikidata 运行此 SPARQL 查询 (results):

SELECT
  ?elementLabel
  ?symbol
  ?article
WHERE
{
  ?element wdt:P31 wd:Q11344;
           wdt:P1086 ?n;
           wdt:P246 ?symbol.
  OPTIONAL {
    ?article schema:about ?element;
             schema:inLanguage "en";
             schema:isPartOf <https://en.wikipedia.org/>.
  }
  FILTER (?n >= 1 && ?n <= 118).
  SERVICE wikibase:label { bd:serviceParam wikibase:language "en" . }
}
ORDER BY ?n

很抱歉,如果这不能直接回答您的问题,但这应该有助于人们以干净的方式抓取相同的信息。

【讨论】:

    猜你喜欢
    • 2014-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多