I'm trying to make a table from HTML content. I've made an example HTML doc to show exactly what the issue is - so although there are many easier ways to accomplish what I'm asking in this example, I have to do it this way (make separate vectors) given the actual larger HTML doc I'm dealing with.
Basically I need to extract specific values from top rows and bottom rows in a weirdly formatted table. Sometimes, however, there aren't values available in a specific row/column (not even dummy blank values), so I can't setup a table because variables are different lengths.
Example:
library(XML)
library(rvest)
htmlEx <- read_html(
'<table>
<thead>
<tbody>
<tr class="top">
<td class="price">
<span class="data-value"> 150 </span>
<small class="name"> Good1 </small>
</td>
</tr>
<tr class="bottom">
<td class="price">
<small class="name"> Good2 </small>
</td>
</tr>
<tr class="top">
<td class="price">
<span class="data-value"> 130 </span>
<small class="name"> Good3 </small>
</td>
</tr>
<tr class="bottom">
<td class="price">
<span class="data-value"> 180 </span>
<small class="name"> Good4 </small>
</td>
</tr>
</tbody>
</thead>
</table>'
)
htmlEx <- htmlTreeParse(htmlEx, useInternalNodes=T)
topVals <- trimws((xpathApply(htmlEx, paste('//*[contains(@class, "top")]//span', sep = ''), xmlValue)))
topNames <- trimws((xpathApply(htmlEx, paste('//*[contains(@class, "top")]//small', sep = ''), xmlValue)))
bottomVals <- trimws((xpathApply(htmlEx, paste('//*[contains(@class, "bottom")]//span', sep = ''), xmlValue)))
bottomNames <- trimws((xpathApply(htmlEx, paste('//*[contains(@class, "bottom")]//small', sep = ''), xmlValue)))
Since there isn't a data-value for the first "bottom" (for Good2), bottomVals is of length 1 so I can't compile a dataframe.
Ideally I'd like to change my xpathApply search so that if there is no <span> under <td class="price"> then it would show up as NA or "". My actual HTML has around 50 different rows with about 5-10 different values missing in different rows/columns, so I can't clean it with logic such as "if length bottomVals != length topVals then append an NA" because every day the order of missing data changes.
Is there a relatively easy fix to my xpath search to accomplish this, or will I have to change my approach completely?
EDIT:
My desired output would for this example is for bottomVals to be [NA, 180], as there is no value for the first class="bottom". This way I can combine everything into a dataframe (data.frame(topNames, bottomNames, topVals, bottomVals)) since they're all of length 2. And to generalize, is there a way to look for a specific element and have it be NA if it doesn't exist. E.g. if I tried looking for a div instead of small/span I'd get [NA, NA].
I know this seems like a roundabout way to turn it into a dataframe, but it really is the easiest way given the actual DOM I'm working with (it's very unorganized and I have to do lots of data cleaning before compiling).