我有网络抓取这种棘手的情况。我想要捕获一些在DOM中非常复杂地存储的特定文本值。我是XPath的新手已经完成了其基础知识和其他一些问题,但无法解决这个问题。 我将使用2张图片来解释这个。
要捕获的数据:
以下是其HTML结构:
<h3>Alias names of NEUROD2 Gene</h3>
<div class="some-col-name-8">
<ul class="list-unstyled list-spacious">
<li>
<span id="aliasMainName">Neuronal Differentiation 2</span>
<sup>...</sup>
<sup>...</sup>
.
.
<sup></sup>
</li>
<li>
"
Text11 "
<sup></sup>
<sup></sup>
</li>
<li>...</li>
<li>
<span class="hilite">NeuroD</span>
"-Related Factor"
<sup>...</sup>
<sup>
<a class="usp we-we-link" target="_blank" href="http:www.uniprot.org/uniprot/23423" title="Uniprot">
</a>
</sup>
</li>
<li>...</li>
因此,您可以在上面看到 li 标记的动态数字之间存在的文字。这里需要的文字没有上标数字。
那就是我想跳过它的孩子 sup 标签。 就像 NeuroD相关因子&amp; NeuroD2 在上图中,我想要 NeuroD相关因子&amp; NeuroD2 作为一个文本,不是NeuroD与“相关因子”和“2”分开。
我用过:
//*[@id="some_id"]/div[1]/div[1]/div[1]/div/ul/li/*
。
哪个只提供 li 的孩子,这解决了 sup 标签的跳过,但它也省略了像这样的元素 - 相关字段和 2 。在 NeuroD 。
如何解决XPath这样的问题。 请高度赞赏任何建议。
答案 0 :(得分:2)
我想你是在谈论这个页面http://www.genecards.org/cgi-bin/carddisp.pl?gene=NEUROD2
在纯XPath中,您将应用类似
的内容//li//text()[not(ancestor::sup)]
从每个li
忽略来自sup
的文字中获取文字...但Selenium不支持此语法。
作为最后的手段,您可以应用少量黑客来隐藏sup
个节点,并只获取每个li
所需的文字:
driver.execute_script("""document.querySelectorAll("#aliases_descriptions sup").forEach(function(i)
{i.setAttribute("style", "visibility: hidden");});""")
required_text_nodes = [li.text for li in driver.find_elements_by_xpath('//section[@id="aliases_descriptions"]//h3[.="Aliases for NEUROD2 Gene"]/following-sibling::div//li')]
print(required_text_nodes)
的输出:
['Neuronal Differentiation 2', 'Class A Basic Helix-Loop-Helix Protein 1', 'Neurogenic Differentiation 2', 'NeuroD-Related Factor', 'BHLHa1', 'NDRF', 'Neurogenic Basic-Helix-Loop-Helix Protein', 'Neurogenic Differentiation Factor 2', 'NeuroD2']
P.S。获得文本后,您可以再次显示sup
个节点:
driver.execute_script("""document.querySelectorAll("#aliases_descriptions sup").forEach(function(i)
{i.setAttribute("style", "visibility: visible");});""")
答案 1 :(得分:0)
AFAIK,没有直接的方法来使用XPath,我可能是错的。但是你可以使用几个循环来完成所需的操作。您可以使用以下逻辑: -
li
li
的文字,这将是sup
标记所需的文字+文字li
的所有孩子并循环播放这些孩子以获取sup
代码的文字。我从未使用过python,我的语法可能有问题,但这就是我认为的方式 -
listParent = driver.find_element_by_xpath('//*[@id]') # Id of parent of the list.
list = listParent.find_elements_by_xpath('//li')
for li in list:
data = li.text
sups = li.find_elements_by_xpath('.//sup')
text=''
for sup in sups
text = text + sup.text
requiredText = string.replace(data, text, "")
答案 2 :(得分:0)
您可以使用以下xpath排除sup标记。
//*[@id="some_id"]/div[1]/div[1]/div[1]/div/ul/li/node()[not(local-name()="sup"]
只是xpath,
//ul/li/node()[not(local-name()="sup"]
它还返回包含文本节点的所有节点。但是selenium不支持文本节点作为java中的返回类型。我们可以借助javascript。以下代码可能有效。
System.setProperty("webdriver.chrome.driver", "C:\\Projects\\SeleniumDrivers\\chromedriver.exe");
driver = new ChromeDriver();
JavascriptExecutor jse = (JavascriptExecutor)driver;
driver.get("file:///C:/Projects/testing2.html");
String value="";
String script;
List<WebElement> lstElements=driver.findElements(By.xpath("//ul/li"));
for(int i=1;i<=lstElements.size();i++){
script="var iterator=document.evaluate('//ul/li["+i+"]/node()[not(local-name()=\"sup\")]', document, null, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);"+
" var text='';"+
"try {"+
"var thisNode = iterator.iterateNext();"+
"while (thisNode) {"+
"text=text.concat(thisNode.textContent);"+
"thisNode = iterator.iterateNext();"+
"}"+
"}"+
"catch (e) {"+
" dump('Error: Document tree modified during iteration ' + e );"+
"} return text;";
value = (String)jse.executeScript(script);
System.out.println(value);
}
driver.quit();
您可以更改python的脚本。它应该工作